{"id":21023206,"url":"https://github.com/simerplaha/justsql","last_synced_at":"2025-09-23T23:17:33.532Z","repository":{"id":49845049,"uuid":"513383715","full_name":"simerplaha/JustSQL","owner":"simerplaha","description":"Just write SQL as String","archived":false,"fork":false,"pushed_at":"2022-09-25T03:50:03.000Z","size":267,"stargazers_count":2,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-13T18:14:17.665Z","etag":null,"topics":["database-access","database-connector","database-management","scala","sql"],"latest_commit_sha":null,"homepage":"","language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/simerplaha.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}},"created_at":"2022-07-13T04:49:00.000Z","updated_at":"2023-10-14T14:37:57.000Z","dependencies_parsed_at":"2023-01-18T21:15:28.985Z","dependency_job_id":null,"html_url":"https://github.com/simerplaha/JustSQL","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/simerplaha/JustSQL","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simerplaha%2FJustSQL","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simerplaha%2FJustSQL/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simerplaha%2FJustSQL/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simerplaha%2FJustSQL/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/simerplaha","download_url":"https://codeload.github.com/simerplaha/JustSQL/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simerplaha%2FJustSQL/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":276662948,"owners_count":25682177,"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-09-23T02:00:09.130Z","response_time":73,"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":["database-access","database-connector","database-management","scala","sql"],"created_at":"2024-11-19T11:17:13.119Z","updated_at":"2025-09-23T23:17:33.493Z","avatar_url":"https://github.com/simerplaha.png","language":"Scala","readme":"# JustSQL\n\nJust write SQL as `String` and parse results into types.\n\nA lightweight SQL library that adds type-safety to query results \u0026 parameters,\nallowing plain SQL queries.\n\nCan be used in parallel with other libraries.\nInterop for [Slick](#slick-interop) or [HikariCP](#hikaricp-interop) is provided.\n\nSmall: 407KB jar file. No external core dependency.\n\n## Why another SQL library?\n\n- ORMs, DSLs and custom string interpolation solutions are nice, but most are incomplete and restrictive, specially\n  when writing complex SQL queries.\n- Debugging performance issues in ORM generated queries, translating back and forth between SQL and ORM types is\n  time-consuming.\n- Many ORMs do not have any support for `EXPLAIN ANALYZE` statements.\n- IDEs have much better support/plugins for executing and analysing plain SQL queries versus custom DSLs.\n\n# Sponsors\n\nThank you [JetBrains](https://www.jetbrains.com/?from=SwayDB)\n\u0026 [JProfiler](https://www.ej-technologies.com/products/jprofiler/overview.html)\nfor full-featured open-source licences to their awesome development tools!\n\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\u003ca href=\"https://www.jetbrains.com/?from=SwayDB\" target=\"_blank\"\u003e\u003cimg src=\"/docs/jetbrains_logo.png\" alt=\"Jetbrains support\"/\u003e\u003c/a\u003e\u003c/td\u003e\n    \u003ctd\u003e\u003ca href=\"https://www.ej-technologies.com/products/jprofiler/overview.html\" target=\"_blank\"\u003e\u003cimg src=\"/docs/jprofiler_logo.png\" alt=\"JProfiler support\"/\u003e\u003c/a\u003e\u003c/td\u003e\n    \u003ctd\u003e\u003ca href=\"https://github.com/sponsors/simerplaha\" target=\"_blank\"\u003e[Become a sponsor]\u003c/a\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n# _NOTE: The following documentation and release is WIP_\n\n[//]: # (# Setup)\n\n[//]: # ()\n\n[//]: # (```scala)\n\n[//]: # (libraryDependencies ++= Seq\u0026#40;)\n\n[//]: # (  \"com.github.simerplaha\" %% \"justsql\" % \"0.1.0\",)\n\n[//]: # (  //Optional: For Slick interop)\n\n[//]: # (  \"com.github.simerplaha\" %% \"justsql-slick\" % \"0.1.0\",)\n\n[//]: # (  //Optional: For hikariCP interop)\n\n[//]: # (  \"com.github.simerplaha\" %% \"justsql-hikari\" % \"0.1.0\")\n\n[//]: # (\u0026#41;)\n\n[//]: # (```)\n\n# Quick start\n\nSee quick-start [Example.scala](/justsql/src/test/scala/example/Example.scala).\n\n# Create JustSQL\n\nI'm using Postgres and the default `JavaSQLConnector` here, but you should use a high-performance\nJDBC connection pool library. See interop for [Slick](#slick-interop) or [HikariCP](#hikaricp-interop).\n\nA `JustSQL` instance is only required when executing a query i.e. when invoking `runSync()` or `runAsync()`.\n\nEverywhere else queries are declarative, so you define your queries without executing them so they can be\n[embedded](#embed-queries) and [composed](#compose-queries).\n\n```scala\nimport justsql._ //single import\n\nimplicit val db = JustSQL(datasource = JavaSQLConnector()) //create database instance\n```\n\n# `update()`\n\nQueries that mutate like `CREATE, INSERT OR UPDATE` queries are executed via `update()` function.\n\nLet's create our example `USERS` table.\n\n```scala\n//create table\nval create: Try[Int] = \"CREATE TABLE USERS (id INT, name VARCHAR)\".update().runSync()\n//insert rows\nval insert: Try[Int] =\n  \"\"\"\n    |INSERT INTO USERS (id, name)\n    |           VALUES (1, 'Harry'),\n    |                  (2, 'Ayman')\n    |\"\"\".stripMargin.update().runSync()\n```\n\n## for-comprehension\n\nOr execute the above queries using `for`-comprehension\n\n```scala\nval createAndInsert: Sql[(Int, Int)] =\n  for {\n    create \u003c- \"CREATE TABLE USERS (id INT, name VARCHAR)\".update()\n    insert \u003c- \"INSERT INTO USERS (id, name) VALUES (1, 'Harry'), (2, 'Ayman')\".update()\n  } yield (create, insert)\n\nval result: Try[(Int, Int)] = createAndInsert.runSync()\n```\n\n# Query parameters\n\nSQL parameters are set with the suffix `?`.\n\nThe above `INSERT` query can be written with parameters as following\n\n```scala\n//Or insert using parameters\nval insertParametric: Try[Int] =\n  UpdateSQL {\n    implicit params =\u003e\n      s\"\"\"\n         |INSERT INTO USERS (id, name)\n         |           VALUES (${1.?}, ${\"Harry\".?}),\n         |                  (${2.?}, ${\"Ayman\".?})\n         |\"\"\".stripMargin\n  }.runSync()\n```\n\n# Transactionally\n\nBeing just SQL, transactions are written with the usual `BEGIN;` and `COMMIT;` statements.\n\n```scala\nval transaction: Try[Int] =\n  UpdateSQL {\n    implicit params =\u003e\n      s\"\"\"\n         |BEGIN;\n         |\n         |CREATE TABLE USERS (id INT, name VARCHAR);\n         |\n         |INSERT INTO USERS (id, name)\n         |           VALUES (${1.?}, ${\"Harry\".?}),\n         |                  (${2.?}, ${\"Ayman\".?});\n         |\n         |COMMIT;\n         |\"\"\".stripMargin\n  }.recoverWith {\n    _ =\u003e\n      \"ROLLBACK\".update() //if there was an error rollback\n  }.runSync()\n```\n\n# `select()`\n\nFirst, we need to create a `case class` that represents a table row, which in our case is a `User`\n\n```scala\n//case class that represents a table row\ncase class User(id: Int, name: String)\n//Build a row reader for User\nimplicit val userReader = RowReader(User.tupled)\n```\n\nRead all `User`s\n\n```scala\nval users: Try[ArraySeq[User]] = \"SELECT * FROM USERS\".select[User]().runSync()\n```\n\n## Or if you want a `List`, provide it as a type argument\n\n```scala\nval usersCollected: Try[List[User]] = \"SELECT * FROM USERS\".select[User, List]().runSync()\n```\n\n## Or with Parameters\n\n```scala\nval usersParametric: SelectSQL[String, ArraySeq] =\n  SelectSQL[String] {\n    implicit params: Params =\u003e\n      s\"\"\"\n         |SELECT name from USERS where id = ${1.?}\n         |\"\"\".stripMargin\n  }\n```\n\n# `head()`, `headOption()`, `exactlyOne()`\n\nReturns `Some(first element)` from the query result or-else `None` if empty\n\n```scala\nval headOption: Try[Option[User]] = \"SELECT * FROM USERS\".select[User]().headOption().runSync()\n```\n\nFirst element from the query result\n\n```scala\nval head: Try[Int] = \"SELECT max(id) FROM USERS\".select[Int]().head().runSync()\n```  \n\nExpects always one row in the result. If there are more than one, returns a failure.\n\n```scala\nval exactlyOne: Try[Int] = \"SELECT count(*) FROM USERS\".select[Int]().exactlyOne().runSync()\n```\n\n# Embed queries - `embed`\n\nEmbed queries using `embed` function.\n\n```scala\nval query1: SelectSQL[Int, ArraySeq] =\n  \"SELECT max(id) from USERS\".select[Int]()\n\n//This query embeds query1 by calling `query1.embed`\nval query2: Try[ArraySeq[String]] =\n  SelectSQL[String] {\n    implicit params: Params =\u003e\n      s\"\"\"\n         |SELECT name from USERS\n         | WHERE id = (${query1.embed})\n         |\"\"\".stripMargin\n  }.runSync()\n```\n\n# Compose queries\n\nTODO\n\n# Sequence\n\nRun multiple queries in the same connection\n\n```scala\nval sequence: SQL[Seq[Int]] =\n  SQL.sequence(\n    \"SELECT 1\".select[Int](),\n    \"SELECT 2\".select[Int](),\n    \"SELECT 3\".select[Int]()\n  ).map(_.flatten)\n```\n\n# Failed or Success `SQL[T]`\n\nCreate a successful SQL\n\n```scala\nval success: SQL[Int] =\n  SQL.success[Int](123)\n```\n\nCreate a failed SQL\n\n```scala\nval failed: SQL[Int] =\n  SQL.failure[Int](new Exception(\"Something went wrong\"))\n```\n\n# Custom `ParamWriter`\n\n## `ParamWriter` - Data types with single or multiple JDBC parameters\n\nTODO\n\n## `OneParamWriter` - Data types with single JDBC parameter\n\n```scala\n//My custom data types\ncase class MyColumn(int: Int)\n\n//Writer. See ParamWriter for more examples.\nval paramWriter: OneParamWriter[MyColumn] =\n  (statement: PositionedPreparedStatement, myColumn: MyColumn) =\u003e\n    statement setInt myColumn.int\n```\n\n# Custom `RowReader` and `ColReader`\n\nA SQL table is just a bunch of a rows and columns right. So we have a `RowReader` and a `ColReader` to represent those.\n\n## `RowReader`\n\nA `RowReader` is just a collection of one or many `ColReader(s)`.\n\nTODO\n\n## `ColReader`\n\n```scala\n//custom column\ncase class MyColumn(int: Int)\n\n//custom column reader\nval colReader: ColReader[MyColumn] =\n  (resultSet: ResultSet, index: Int) =\u003e\n    MyColumn(resultSet.getInt(1))\n```\n\n# Slick interop\n\nMake sure the dependency [`justsql-slick`](#setup) is in your build.\n\nThis allows JustSQL to borrow connections created by Slick.\n\n```scala\n//Your Slick database-config \nval dbConfig: DatabaseConfig[JdbcProfile] = ???\n//Just pass it onto JustSQL\nimplicit val justSQL = JustSQL(SlickSQLConnector(dbConfig))\n```\n\n# HikariCP interop\n\nMake sure the dependency [`justsql-hikari`](#setup) is in your build.\n\n```scala\n//Pass HikariSQLConnector to JustSQL\nimplicit val justSQL = JustSQL(HikariSQLConnector())\n```\n\n# Unsafe\n\nUnsafe APIs give direct access to low level `java.sql.ResultSet` type.\n\n## unsafeSelect()\n\n```scala\n//read the names of all Users\nval names: Try[Array[String]] = \"SELECT * FROM USERS\".unsafeSelect(_.getString(\"name\")).runSync()\n```\n\n","funding_links":["https://github.com/sponsors/simerplaha"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsimerplaha%2Fjustsql","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsimerplaha%2Fjustsql","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsimerplaha%2Fjustsql/lists"}