2019-06-25 12:56:10 +02:00
# Jet
2019-06-23 16:48:05 +02:00
2019-08-18 16:42:51 +02:00
[](https://circleci.com/gh/go-jet/jet/tree/develop)
[](https://codecov.io/gh/go-jet/jet)
2019-07-18 18:51:13 +02:00
[](https://goreportcard.com/report/github.com/go-jet/jet)
2019-07-18 17:43:11 +02:00
[](http://godoc.org/github.com/go-jet/jet)
2019-07-23 14:10:56 +02:00
[](https://github.com/go-jet/jet/releases)
2019-07-23 13:56:05 +02:00
2019-06-23 16:48:05 +02:00
2019-08-18 16:42:51 +02:00
Jet is a framework for writing type-safe SQL queries in Go, with ability to easily
2019-08-20 09:45:56 +02:00
convert database query result into desired arbitrary object structure.
Jet currently supports `PostgreSQL` , `MySQL` and `MariaDB` . Future releases will add support for additional databases.
2019-07-14 14:30:39 +02:00
2019-08-19 16:24:15 +02:00

2019-09-17 13:34:47 +02:00
Jet is the easiest and the fastest way to write complex SQL queries and map database query result
2019-08-19 16:24:15 +02:00
into complex object composition. __It is not an ORM.__
2019-06-24 18:22:55 +02:00
2019-09-08 10:50:47 +02:00
## Motivation
https://medium.com/@go .jet/jet-5f3667efa0cc
2019-06-26 12:52:25 +02:00
## Contents
2019-07-14 14:30:39 +02:00
- [Features ](#features )
2019-06-26 12:52:25 +02:00
- [Getting Started ](#getting-started )
- [Prerequisites ](#prerequisites )
- [Installation ](#installation )
- [Quick Start ](#quick-start )
- [Generate sql builder and model files ](#generate-sql-builder-and-model-files )
- [Lets write some SQL queries in Go ](#lets-write-some-sql-queries-in-go )
2019-07-14 14:30:39 +02:00
- [Execute query and store result ](#execute-query-and-store-result )
2019-06-26 12:52:25 +02:00
- [Benefits ](#benefits )
2019-07-04 14:12:59 +02:00
- [Dependencies ](#dependencies )
2019-06-26 12:52:25 +02:00
- [Versioning ](#versioning )
2019-07-16 12:17:27 +02:00
- [License ](#license )
2019-07-02 10:31:52 +02:00
2019-06-26 12:52:25 +02:00
## Features
2019-08-18 16:42:51 +02:00
1) Auto-generated type-safe SQL Builder
- PostgreSQL:
* SELECT `(DISTINCT, FROM, WHERE, GROUP BY, HAVING, ORDER BY, LIMIT, OFFSET, FOR, UNION, INTERSECT, EXCEPT, sub-queries)`
* INSERT `(VALUES, query, RETURNING)` ,
* UPDATE `(SET, WHERE, RETURNING)` ,
* DELETE `(WHERE, RETURNING)` ,
* LOCK `(IN, NOWAIT)`
- MySQL and MariaDB:
* SELECT `(DISTINCT, FROM, WHERE, GROUP BY, HAVING, ORDER BY, LIMIT, OFFSET, FOR, UNION, LOCK_IN_SHARE_MODE, sub-queries)`
* INSERT `(VALUES, query)` ,
* UPDATE `(SET, WHERE)` ,
* DELETE `(WHERE, ORDER_BY, LIMIT)` ,
* LOCK `(READ, WRITE)`
2019-09-21 15:48:14 +02:00
2) Auto-generated Data Model types - Go types mapped to database type (table, view or enum), used to store
2019-07-23 13:40:39 +02:00
result of database queries. Can be combined to create desired query result destination.
2019-07-23 13:29:38 +02:00
3) Query execution with result mapping to arbitrary destination structure.
2019-06-24 18:22:55 +02:00
2019-06-26 12:52:25 +02:00
## Getting Started
2019-06-24 18:22:55 +02:00
2019-06-26 12:52:25 +02:00
### Prerequisites
2019-06-24 18:22:55 +02:00
To install Jet package, you need to install Go and set your Go workspace first.
[Go ](https://golang.org/ ) **version 1.8+ is required**
2019-06-26 12:52:25 +02:00
### Installation
2019-06-24 18:22:55 +02:00
Use the bellow command to install jet
```sh
$ go get -u github.com/go-jet/jet
```
2019-07-14 14:30:39 +02:00
Install jet generator to GOPATH bin folder. This will allow generating jet files from the command line.
2019-06-24 18:22:55 +02:00
```sh
2019-07-08 10:48:03 +02:00
go install github.com/go-jet/jet/cmd/jet
2019-06-24 18:22:55 +02:00
```
Make sure GOPATH bin folder is added to the PATH environment variable.
2019-06-26 12:52:25 +02:00
### Quick Start
2019-08-18 16:42:51 +02:00
For this quick start example we will use PostgreSQL sample _'dvd rental'_ database. Full database dump can be found in [./tests/testdata/init/postgres/dvds.sql ](./tests/testdata/init/postgres/dvds.sql ).
2019-06-24 18:22:55 +02:00
Schema diagram of interest for example can be found [here ](./examples/quick-start/diagram.png ).
2019-07-23 13:29:38 +02:00
#### Generate SQL Builder and Model files
2019-08-18 16:42:51 +02:00
To generate jet SQL Builder and Data Model files from postgres database, we need to call `jet` generator with postgres
2019-07-14 14:30:39 +02:00
connection parameters and root destination folder path for generated files.\
2019-07-23 13:29:38 +02:00
Assuming we are running local postgres database, with user `jetuser` , user password `jetpass` , database `jetdb` and
schema `dvds` we will use this command:
2019-06-24 18:22:55 +02:00
```sh
2019-08-18 16:42:51 +02:00
jet -source=PostgreSQL -host=localhost -port=5432 -user=jetuser -password=jetpass -dbname=jetdb -schema=dvds -path=./gen
2019-06-24 18:22:55 +02:00
```
```sh
2019-07-23 13:29:38 +02:00
Connecting to postgres database: host=localhost port=5432 user=jetuser password=jetpass dbname=jetdb sslmode=disable
2019-06-24 18:22:55 +02:00
Retrieving schema information...
2019-09-21 15:48:14 +02:00
FOUND 15 table(s), 7 view(s), 1 enum(s)
Cleaning up destination directory...
2019-06-24 18:22:55 +02:00
Generating table sql builder files...
2019-09-21 15:48:14 +02:00
Generating view sql builder files...
2019-06-24 18:22:55 +02:00
Generating enum sql builder files...
2019-09-21 15:48:14 +02:00
Generating table model files...
Generating view model files...
2019-06-24 18:22:55 +02:00
Generating enum model files...
Done
```
2019-08-18 16:42:51 +02:00
Procedure is similar for MySQL or MariaDB, except source should be replaced with `MySql` or `MariaDB` and schema name should
be omitted (both databases doesn't have schema support).
_*User has to have a permission to read information schema tables._
2019-07-14 14:30:39 +02:00
As command output suggest, Jet will:
2019-09-21 15:48:14 +02:00
- connect to postgres database and retrieve information about the _tables_ , _views_ and _enums_ of `dvds` schema
2019-07-14 14:30:39 +02:00
- delete everything in schema destination folder - `./gen/jetdb/dvds` ,
2019-09-21 15:48:14 +02:00
- and finally generate SQL Builder and Model files for each schema table, view and enum.
2019-06-25 13:09:00 +02:00
Generated files folder structure will look like this:
2019-06-25 12:56:10 +02:00
```sh
2019-07-14 14:30:39 +02:00
|-- gen # -path
2019-06-25 12:56:10 +02:00
| `-- jetdb # database name
| `-- dvds # schema name
2019-09-21 15:48:14 +02:00
| |-- enum # sql builder package for enums
2019-06-25 13:09:00 +02:00
| | |-- mpaa_rating.go
2019-09-21 15:48:14 +02:00
| |-- table # sql builder package for tables
2019-06-25 12:56:10 +02:00
| |-- actor.go
| |-- address.go
| |-- category.go
2019-07-14 14:30:39 +02:00
| ...
2019-09-21 15:48:14 +02:00
| |-- view # sql builder package for views
| |-- actor_info.go
| |-- film_list.go
| ...
| |-- model # data model types for each table, view and enum
2019-06-25 12:56:10 +02:00
| | |-- actor.go
| | |-- address.go
2019-07-14 14:30:39 +02:00
| | |-- mpaa_rating.go
| | ...
2019-06-25 12:56:10 +02:00
```
2019-09-21 15:48:14 +02:00
Types from `table` , `view` and `enum` are used to write type safe SQL in Go, and `model` types can be combined to store
2019-07-14 14:30:39 +02:00
results of the SQL queries.
2019-08-18 16:42:51 +02:00
2019-06-26 12:52:25 +02:00
#### Lets write some SQL queries in Go
2019-06-24 18:22:55 +02:00
2019-07-14 14:30:39 +02:00
First we need to import jet and generated files from previous step:
2019-06-24 18:22:55 +02:00
```go
import (
2019-08-18 16:42:51 +02:00
// dot import so go code would resemble as much as native SQL
2019-07-14 14:30:39 +02:00
// dot import is not mandatory
2019-08-18 16:42:51 +02:00
. "github.com/go-jet/jet/examples/quick-start/.gen/jetdb/dvds/table"
. "github.com/go-jet/jet/postgres"
2019-06-24 18:22:55 +02:00
"github.com/go-jet/jet/examples/quick-start/gen/jetdb/dvds/model"
)
```
2019-07-11 17:45:23 +02:00
Lets say we want to retrieve the list of all _actors_ that acted in _films_ longer than 180 minutes, _film language_ is 'English'
and _film category_ is not 'Action'.
2019-06-24 18:22:55 +02:00
```go
stmt := SELECT(
2019-07-14 14:30:39 +02:00
Actor.ActorID, Actor.FirstName, Actor.LastName, Actor.LastUpdate, // or just Actor.AllColumns
Film.AllColumns,
2019-06-25 12:56:10 +02:00
Language.AllColumns,
Category.AllColumns,
).FROM(
Actor.
2019-07-14 14:30:39 +02:00
INNER_JOIN(FilmActor, Actor.ActorID.EQ(FilmActor.ActorID)).
INNER_JOIN(Film, Film.FilmID.EQ(FilmActor.FilmID)).
2019-06-25 12:56:10 +02:00
INNER_JOIN(Language, Language.LanguageID.EQ(Film.LanguageID)).
INNER_JOIN(FilmCategory, FilmCategory.FilmID.EQ(Film.FilmID)).
INNER_JOIN(Category, Category.CategoryID.EQ(FilmCategory.CategoryID)),
).WHERE(
2019-07-14 14:30:39 +02:00
Language.Name.EQ(String("English")).
AND(Category.Name.NOT_EQ(String("Action"))).
AND(Film.Length.GT(Int(180))),
2019-06-25 12:56:10 +02:00
).ORDER_BY(
Actor.ActorID.ASC(),
Film.FilmID.ASC(),
)
2019-06-24 18:22:55 +02:00
```
2019-09-21 15:48:14 +02:00
_Package(dot) import is used so that statement would resemble as much as possible as native SQL._
Note that every column has a type. String column `Language.Name` and `Category.Name` can be compared only with
2019-07-14 14:30:39 +02:00
string columns and expressions. `Actor.ActorID` , `FilmActor.ActorID` , `Film.Length` are integer columns
and can be compared only with integer columns and expressions.
2019-07-23 13:29:38 +02:00
__How to get parametrized SQL query from statement?__
2019-06-24 18:22:55 +02:00
```go
2019-08-18 16:42:51 +02:00
query, args := stmt.Sql()
2019-06-24 18:22:55 +02:00
```
2019-07-14 14:30:39 +02:00
query - parametrized query\
args - parameters for the query
2019-06-24 18:22:55 +02:00
2019-06-25 12:56:10 +02:00
< details >
2019-07-23 13:40:39 +02:00
< summary > Click to see `query` and `args` </ summary >
2019-06-25 12:56:10 +02:00
2019-06-25 13:09:00 +02:00
```sql
2019-06-25 12:56:10 +02:00
SELECT actor.actor_id AS "actor.actor_id",
actor.first_name AS "actor.first_name",
actor.last_name AS "actor.last_name",
actor.last_update AS "actor.last_update",
film.film_id AS "film.film_id",
film.title AS "film.title",
film.description AS "film.description",
film.release_year AS "film.release_year",
film.language_id AS "film.language_id",
film.rental_duration AS "film.rental_duration",
film.rental_rate AS "film.rental_rate",
film.length AS "film.length",
film.replacement_cost AS "film.replacement_cost",
film.rating AS "film.rating",
film.last_update AS "film.last_update",
film.special_features AS "film.special_features",
film.fulltext AS "film.fulltext",
language.language_id AS "language.language_id",
language.name AS "language.name",
language.last_update AS "language.last_update",
category.category_id AS "category.category_id",
category.name AS "category.name",
category.last_update AS "category.last_update"
FROM dvds.actor
INNER JOIN dvds.film_actor ON (actor.actor_id = film_actor.actor_id)
INNER JOIN dvds.film ON (film.film_id = film_actor.film_id)
INNER JOIN dvds.language ON (language.language_id = film.language_id)
INNER JOIN dvds.film_category ON (film_category.film_id = film.film_id)
INNER JOIN dvds.category ON (category.category_id = film_category.category_id)
WHERE ((language.name = $1) AND (category.name != $2)) AND (film.length > $3)
ORDER BY actor.actor_id ASC, film.film_id ASC;
2019-06-25 13:09:00 +02:00
```
```sh
2019-06-25 12:56:10 +02:00
[English Action 180]
```
2019-06-25 13:09:00 +02:00
2019-06-25 12:56:10 +02:00
< / details >
2019-07-14 14:30:39 +02:00
2019-07-23 13:29:38 +02:00
__How to get debug SQL from statement?__
```go
2019-08-18 16:42:51 +02:00
debugSql := stmt.DebugSql()
2019-06-24 18:22:55 +02:00
```
2019-08-19 10:40:34 +02:00
debugSql - query string that can be copy pasted to sql editor and executed. __It's not intended to be used in production!!!__
2019-07-23 13:29:38 +02:00
2019-06-25 12:56:10 +02:00
< details >
2019-07-14 14:30:39 +02:00
< summary > Click to see debug sql< / summary >
2019-06-25 12:56:10 +02:00
2019-06-25 13:09:00 +02:00
```sql
2019-06-25 12:56:10 +02:00
SELECT actor.actor_id AS "actor.actor_id",
actor.first_name AS "actor.first_name",
actor.last_name AS "actor.last_name",
actor.last_update AS "actor.last_update",
film.film_id AS "film.film_id",
film.title AS "film.title",
film.description AS "film.description",
film.release_year AS "film.release_year",
film.language_id AS "film.language_id",
film.rental_duration AS "film.rental_duration",
film.rental_rate AS "film.rental_rate",
film.length AS "film.length",
film.replacement_cost AS "film.replacement_cost",
film.rating AS "film.rating",
film.last_update AS "film.last_update",
film.special_features AS "film.special_features",
film.fulltext AS "film.fulltext",
language.language_id AS "language.language_id",
language.name AS "language.name",
language.last_update AS "language.last_update",
category.category_id AS "category.category_id",
category.name AS "category.name",
category.last_update AS "category.last_update"
FROM dvds.actor
INNER JOIN dvds.film_actor ON (actor.actor_id = film_actor.actor_id)
INNER JOIN dvds.film ON (film.film_id = film_actor.film_id)
INNER JOIN dvds.language ON (language.language_id = film.language_id)
INNER JOIN dvds.film_category ON (film_category.film_id = film.film_id)
INNER JOIN dvds.category ON (category.category_id = film_category.category_id)
WHERE ((language.name = 'English') AND (category.name != 'Action')) AND (film.length > 180)
ORDER BY actor.actor_id ASC, film.film_id ASC;
```
< / details >
2019-06-24 18:22:55 +02:00
2019-07-14 14:30:39 +02:00
#### Execute query and store result
2019-09-17 13:34:47 +02:00
Well formed SQL is just a first half of the job. Lets see how can we make some sense of result set returned executing
2019-07-14 14:30:39 +02:00
above statement. Usually this is the most complex and tedious work, but with Jet it is the easiest.
2019-09-21 15:48:14 +02:00
First we have to create desired structure to store query result.
This is done be combining autogenerated model types or it can be done
manually(see [wiki ](https://github.com/go-jet/jet/wiki/Query-Result-Mapping-(QRM )) for more information).
2019-07-11 17:45:23 +02:00
2019-07-23 13:56:05 +02:00
Let's say this is our desired structure:
2019-06-24 18:22:55 +02:00
```go
var dest []struct {
model.Actor
2019-07-20 11:33:14 +02:00
2019-06-24 18:22:55 +02:00
Films []struct {
model.Film
2019-07-23 13:29:38 +02:00
2019-07-20 11:33:14 +02:00
Language model.Language
Categories []model.Category
2019-06-24 18:22:55 +02:00
}
}
```
2019-09-21 15:48:14 +02:00
`Films` field is a slice because one actor can act in multiple films, and because each film belongs to one language
`Langauge` field is just a single model struct. `Film` can belong to multiple categories.
2019-08-20 09:45:56 +02:00
_*There is no limitation of how big or nested destination can be._
2019-06-24 18:22:55 +02:00
2019-08-18 16:42:51 +02:00
Now lets execute a above statement on open database connection (or transaction) db and store result into `dest` .
2019-06-24 18:22:55 +02:00
```go
err := stmt.Query(db, & dest)
handleError(err)
```
2019-07-15 13:06:06 +02:00
__And thats it.__
2019-07-14 14:30:39 +02:00
`dest` now contains the list of all actors(with list of films acted, where each film has information about language and list of belonging categories) that acted in films longer than 180 minutes, film language is 'English'
2019-06-24 18:22:55 +02:00
and film category is not 'Action'.
Lets print `dest` as a json to see:
```go
jsonText, _ := json.MarshalIndent(dest, "", "\t")
fmt.Println(string(jsonText))
```
2019-07-08 13:00:44 +02:00
```js
2019-06-24 18:22:55 +02:00
[
{
"ActorID": 1,
"FirstName": "Penelope",
"LastName": "Guiness",
"LastUpdate": "2013-05-26T14:47:57.62Z",
"Films": [
{
"FilmID": 499,
"Title": "King Evolution",
"Description": "A Action-Packed Tale of a Boy And a Lumberjack who must Chase a Madman in A Baloon",
"ReleaseYear": 2006,
"LanguageID": 1,
"RentalDuration": 3,
"RentalRate": 4.99,
"Length": 184,
"ReplacementCost": 24.99,
"Rating": "NC-17",
"LastUpdate": "2013-05-26T14:50:58.951Z",
"SpecialFeatures": "{Trailers,\"Deleted Scenes\",\"Behind the Scenes\"}",
"Fulltext": "'action':5 'action-pack':4 'baloon':21 'boy':10 'chase':16 'evolut':2 'king':1 'lumberjack':13 'madman':18 'must':15 'pack':6 'tale':7",
"Language": {
"LanguageID": 1,
"Name": "English ",
"LastUpdate": "2006-02-15T10:02:19Z"
},
2019-06-26 10:30:31 +02:00
"Categories": [
2019-06-24 18:22:55 +02:00
{
"CategoryID": 8,
"Name": "Family",
"LastUpdate": "2006-02-15T09:46:27Z"
}
]
}
]
},
{
"ActorID": 3,
"FirstName": "Ed",
"LastName": "Chase",
"LastUpdate": "2013-05-26T14:47:57.62Z",
"Films": [
{
"FilmID": 996,
"Title": "Young Language",
"Description": "A Unbelieveable Yarn of a Boat And a Database Administrator who must Meet a Boy in The First Manned Space Station",
"ReleaseYear": 2006,
"LanguageID": 1,
"RentalDuration": 6,
"RentalRate": 0.99,
"Length": 183,
"ReplacementCost": 9.99,
"Rating": "G",
"LastUpdate": "2013-05-26T14:50:58.951Z",
"SpecialFeatures": "{Trailers,\"Behind the Scenes\"}",
"Fulltext": "'administr':12 'boat':8 'boy':17 'databas':11 'first':20 'languag':2 'man':21 'meet':15 'must':14 'space':22 'station':23 'unbeliev':4 'yarn':5 'young':1",
"Language": {
"LanguageID": 1,
"Name": "English ",
"LastUpdate": "2006-02-15T10:02:19Z"
},
2019-06-26 10:30:31 +02:00
"Categories": [
2019-06-24 18:22:55 +02:00
{
"CategoryID": 6,
"Name": "Documentary",
"LastUpdate": "2006-02-15T09:46:27Z"
}
]
}
]
},
2019-07-08 13:00:44 +02:00
//...(125 more items)
]
2019-06-24 18:22:55 +02:00
```
2019-07-14 14:30:39 +02:00
What if, we also want to have list of films per category and actors per category, where films are longer than 180 minutes, film language is 'English'
2019-06-24 18:22:55 +02:00
and film category is not 'Action'.
In that case we can reuse above statement `stmt` , and just change our destination:
```go
var dest2 []struct {
model.Category
2019-06-26 10:30:31 +02:00
Films []model.Film
Actors []model.Actor
2019-06-24 18:22:55 +02:00
}
err = stmt.Query(db, & dest2)
handleError(err)
```
< details >
2019-07-14 14:30:39 +02:00
< summary > Click to see `dest2` json</ summary >
2019-06-24 18:22:55 +02:00
2019-07-08 13:00:44 +02:00
```js
2019-06-24 18:22:55 +02:00
[
{
"CategoryID": 8,
"Name": "Family",
"LastUpdate": "2006-02-15T09:46:27Z",
2019-06-26 10:30:31 +02:00
"Films": [
2019-06-24 18:22:55 +02:00
{
"FilmID": 499,
"Title": "King Evolution",
"Description": "A Action-Packed Tale of a Boy And a Lumberjack who must Chase a Madman in A Baloon",
"ReleaseYear": 2006,
"LanguageID": 1,
"RentalDuration": 3,
"RentalRate": 4.99,
"Length": 184,
"ReplacementCost": 24.99,
"Rating": "NC-17",
"LastUpdate": "2013-05-26T14:50:58.951Z",
"SpecialFeatures": "{Trailers,\"Deleted Scenes\",\"Behind the Scenes\"}",
"Fulltext": "'action':5 'action-pack':4 'baloon':21 'boy':10 'chase':16 'evolut':2 'king':1 'lumberjack':13 'madman':18 'must':15 'pack':6 'tale':7"
},
{
"FilmID": 50,
"Title": "Baked Cleopatra",
"Description": "A Stunning Drama of a Forensic Psychologist And a Husband who must Overcome a Waitress in A Monastery",
"ReleaseYear": 2006,
"LanguageID": 1,
"RentalDuration": 3,
"RentalRate": 2.99,
"Length": 182,
"ReplacementCost": 20.99,
"Rating": "G",
"LastUpdate": "2013-05-26T14:50:58.951Z",
"SpecialFeatures": "{Commentaries,\"Behind the Scenes\"}",
"Fulltext": "'bake':1 'cleopatra':2 'drama':5 'forens':8 'husband':12 'monasteri':20 'must':14 'overcom':15 'psychologist':9 'stun':4 'waitress':17"
}
],
2019-06-26 10:30:31 +02:00
"Actors": [
2019-06-24 18:22:55 +02:00
{
"ActorID": 1,
"FirstName": "Penelope",
"LastName": "Guiness",
"LastUpdate": "2013-05-26T14:47:57.62Z"
},
{
"ActorID": 20,
"FirstName": "Lucille",
"LastName": "Tracy",
"LastUpdate": "2013-05-26T14:47:57.62Z"
},
{
"ActorID": 36,
"FirstName": "Burt",
"LastName": "Dukakis",
"LastUpdate": "2013-05-26T14:47:57.62Z"
},
{
"ActorID": 70,
"FirstName": "Michelle",
"LastName": "Mcconaughey",
"LastUpdate": "2013-05-26T14:47:57.62Z"
},
{
"ActorID": 118,
"FirstName": "Cuba",
"LastName": "Allen",
"LastUpdate": "2013-05-26T14:47:57.62Z"
},
{
"ActorID": 187,
"FirstName": "Renee",
"LastName": "Ball",
"LastUpdate": "2013-05-26T14:47:57.62Z"
},
{
"ActorID": 198,
"FirstName": "Mary",
"LastName": "Keitel",
"LastUpdate": "2013-05-26T14:47:57.62Z"
}
]
},
2019-07-08 13:00:44 +02:00
//...
]
2019-06-24 18:22:55 +02:00
```
< / details >
Complete code example can be found at [./examples/quick-start/quick-start.go ](./examples/quick-start/quick-start.go )
2019-06-26 10:30:31 +02:00
2019-09-08 10:50:47 +02:00
This example represent probably the most common use case. Detail info about additional statements, features and use cases can be
found at project [Wiki ](https://github.com/go-jet/jet/wiki ) page.
2019-06-26 10:30:31 +02:00
2019-06-26 12:52:25 +02:00
## Benefits
2019-08-18 16:42:51 +02:00
What are the benefits of writing SQL in Go using Jet?
The biggest benefit is speed. Speed is improved in 3 major areas:
2019-07-17 13:22:14 +02:00
2019-06-26 12:52:25 +02:00
##### Speed of development
2019-07-14 14:30:39 +02:00
2019-09-21 15:48:14 +02:00
Writing SQL queries is faster and easier, because the developers have help of SQL code completion and SQL type safety directly from Go.
Automatic scan to arbitrary structure removes a lot of headache and boilerplate code needed to structure database query result.
2019-07-14 14:30:39 +02:00
2019-06-26 12:52:25 +02:00
##### Speed of execution
2019-07-14 14:30:39 +02:00
2019-09-21 15:48:14 +02:00
While ORM libraries can introduce significant performance penalties due too number of round-trips to the database,
Jet will always perform much better, because of the single database call.
2019-07-11 17:45:23 +02:00
Common web and database server usually are not on the same physical machine, and there is some latency between them.
2019-06-26 12:52:25 +02:00
Latency can vary from 5ms to 50+ms. In majority of cases query executed on database is simple query lasting no more than 1ms.
In those cases web server handler execution time is directly proportional to latency between server and database.
2019-07-14 14:30:39 +02:00
This is not such a big problem if handler calls database couple of times, but what if web server is using ORM to retrieve data from database.
ORM sometimes can access the database once for every object needed. Now lets say latency is 30ms and there are 100
different objects required from the database. This handler will last 3 seconds !!!.
2019-06-27 14:31:57 +02:00
With Jet, handler time lost on latency between server and database is constant. Because we can write complex query and
2019-07-23 13:29:38 +02:00
return result in one database call. Handler execution will be only proportional to the number of rows returned from database.
2019-06-26 12:52:25 +02:00
ORM example replaced with jet will take just 30ms + 'result scan time' = 31ms (rough estimate).
2019-09-21 15:48:14 +02:00
With Jet you can even join the whole database and store the whole structured result in one database call.
2019-07-30 11:45:10 +02:00
This is exactly what is being done in one of the tests: [TestJoinEverything ](/tests/postgres/chinook_db_test.go#L40 ).
2019-07-23 13:29:38 +02:00
The whole test database is joined and query result(~10,000 rows) is stored in a structured variable in less than 0.7s.
2019-06-26 12:52:25 +02:00
##### How quickly bugs are found
2019-07-14 14:30:39 +02:00
2019-06-26 12:52:25 +02:00
The most expensive bugs are the one on the production and the least expensive are those found during development.
2019-09-21 15:48:14 +02:00
With automatically generated type safe SQL, not only queries are written faster but bugs are found sooner.
2019-06-26 12:52:25 +02:00
Lets return to quick start example, and take closer look at a line:
```go
2019-07-14 14:30:39 +02:00
AND(Film.Length.GT(Int(180))),
2019-06-26 12:52:25 +02:00
```
Lets say someone changes column `length` to `duration` from `film` table. The next go build will fail at that line and
the bug will be caught at compile time.
Lets say someone changes the type of `length` column to some non integer type. Build will also fail at the same line
because integer columns and expressions can be only compered to other integer columns and expressions.
2019-09-08 10:50:47 +02:00
Build will also fail if someone removes `length` column from `film` table, because `Film` field will be omitted from SQL Builder and Model types, next time `jet` generator is run.
2019-06-26 12:52:25 +02:00
Without Jet these bugs will have to be either caught by some test or by manual testing.
2019-06-26 10:30:31 +02:00
2019-07-04 14:12:59 +02:00
## Dependencies
2019-07-04 17:54:15 +02:00
At the moment Jet dependence only of:
2019-08-20 09:45:56 +02:00
- `github.com/lib/pq` _(Used by jet generator to read information about database schema from `PostgreSQL`)_
- `github.com/go-sql-driver/mysql` _(Used by jet generator to read information about database from `MySQL` and `MariaDB`)_
2019-08-18 16:42:51 +02:00
- `github.com/google/uuid` _(Used in data model files and for debug purposes)_
2019-07-04 17:54:15 +02:00
To run the tests, additional dependencies are required:
- `github.com/pkg/profile`
- `gotest.tools/assert`
2019-07-04 14:12:59 +02:00
2019-06-26 12:52:25 +02:00
## Versioning
2019-06-24 18:22:55 +02:00
2019-06-26 12:52:25 +02:00
[SemVer ](http://semver.org/ ) is used for versioning. For the versions available, see the [releases ](https://github.com/go-jet/jet/releases ).
2019-06-24 18:22:55 +02:00
2019-07-16 12:17:27 +02:00
## License
Copyright 2019 Goran Bjelanovic
2019-07-23 13:40:39 +02:00
Licensed under the Apache License, Version 2.0.