github.com/RevenueMonster/sqlike@v1.0.6/README.md (about) 1 # sqlike 2 3 [](https://github.com/RevenueMonster/sqlike/actions?query=workflow%3Abuild) 4 [](https://github.com/RevenueMonster/sqlike/releases) 5 [](https://goreportcard.com/report/github.com/RevenueMonster/sqlike) 6 [](https://codecov.io/gh/si3nloong/sqlike) 7 [](https://github.com/RevenueMonster/sqlike/blob/main/LICENSE) 8 [](https://app.fossa.com/projects/git%2Bgithub.com%2Fsi3nloong%2Fsqlike?ref=badge_shield) 9 10 > A Golang SQL ORM which anti-toxic query and focus on the latest features. 11 12 ## 🔨 Installation 13 14 ```console 15 go get github.com/RevenueMonster/sqlike 16 ``` 17 18 Fully compatible with native library `database/sql`, which mean you are allow to use `driver.Valuer` and `sql.Scanner`. 19 20 ## 📻 Legacy Support 21 22 SQLike did support **mysql 5.7** as well. For better compatibility, we suggest you to use at least mysql 8.0. 23 24 ## 🪣 Minimum Requirements 25 26 - **mysql 8.0** and above 27 - **golang 1.15** and above 28 29 ## ❓ Why another ORM? 30 31 - We don't really care about _legacy support_, we want _latest feature_ that mysql and golang offer us 32 - We want to get rid from **toxic query** (also known as slow query) 33 34 ## ✨ What do we provide apart from native package (database/sql)? 35 36 - Support `ENUM` and `SET` 37 - Support `UUID` (^8.0) 38 - Support `JSON` 39 - Support `descending index` (^8.0) 40 - Support `multi-valued` index (^8.0.17) 41 - Support `Spatial` with package [orb](https://github.com/paulmach/orb), such as `Point`, `LineString` 42 - Support `generated column` of `stored column` and `virtual column` 43 - Extra custom type such as `Date`, `Key`, `Boolean` 44 - Support `struct` on `Find`, `FindOne`, `InsertOne`, `Insert`, `ModifyOne`, `DeleteOne`, `Delete`, `DestroyOne` and `Paginate` apis 45 - Support `Transactions` 46 - Support cursor based pagination 47 - Support advance and complex query statement 48 - Support [civil.Date](https://cloud.google.com/go/civil#Date), [civil.Time](https://cloud.google.com/go/civil#Time) and [time.Location](https://pkg.go.dev/time#Time) 49 - Support [language.Tag](https://godoc.org/golang.org/x/text/language#example-Tag--Values) and [currency.Unit](https://godoc.org/golang.org/x/text/currency#Unit) 50 - Support authorization plugin [Casbin](https://github.com/casbin/casbin) 51 - Support tracing plugin [OpenTracing](https://github.com/opentracing/opentracing-go) 52 - Developer friendly, (query is highly similar to native sql query) 53 - Support `sqldump` for backup purpose **(experiment)** 54 55 <!-- You can refer to [examples](https://github.com/RevenueMonster/sqlike/tree/main/examples) folder to see what apis we offer and learn how to use those apis --> 56 57 ## ⚠️ Limitation 58 59 Our main objective is anti toxic query, that why some functionality we doesn't offer out of box 60 61 - offset based pagination (but you may achieve this by using `Limit` and `Offset`) 62 - eager loading (we want to avoid magic function, you should handle this by your own using goroutines) 63 - join (eg. left join, outer join, inner join), join clause is consider as toxic query, you should alway find your record using primary key 64 - left wildcard search using Like is not allow (but you may use `expr.Raw` to bypass it) 65 - bidirectional sorting is not allow (except mysql 8.0 and above) 66 - currently only support `mysql` driver (postgres and sqlite yet to implement) 67 68 ## General APIs 69 70 ```go 71 package main 72 73 import ( 74 "time" 75 "github.com/RevenueMonster/sqlike/sqlike/actions" 76 "github.com/RevenueMonster/sqlike/sqlike" 77 "github.com/RevenueMonster/sqlike/sqlike/options" 78 "github.com/RevenueMonster/sqlike/sql/expr" 79 "github.com/google/uuid" 80 "context" 81 82 _ "github.com/go-sql-driver/mysql" 83 ) 84 85 // UserStatus : 86 type UserStatus string 87 88 const ( 89 UserStatusActive UserStatus = "ACTIVE" 90 UserStatusSuspend UserStatus = "SUSPEND" 91 ) 92 93 type User struct { 94 ID uuid.UUID `sqlike:",primary_key"` 95 ICNo string `sqlike:",generated_column"` // generated column generated by virtual column `Detail.ICNo` 96 Name string `sqlike:",size=200,charset=latin1"` // you can set the data type length and charset with struct tag 97 Email string `sqlike:",unique"` // set to unique 98 Address string `sqlike:",longtext"` // `longtext` is an alias of long text data type in mysql 99 Detail struct { 100 ICNo string `sqlike:",virtual_column=ICNo"` // virtual column 101 PhoneNo string 102 Age uint 103 } 104 Status UserStatus `sqlike:",enum=ACTIVE|SUSPEND"` // enum data type 105 CreatedAt time.Time 106 UpdatedAt time.Time 107 } 108 109 func newUser() (user User) { 110 now := time.Now() 111 user.ID = uuid.New() 112 user.CreatedAt = now 113 user.UpdatedAt = now 114 return 115 } 116 117 func main() { 118 ctx := context.Background() 119 client := sqlike.MustConnect( 120 ctx, 121 "mysql", 122 options.Connect(). 123 SetUsername("root"). 124 SetPassword(""). 125 SetHost("localhost"). 126 SetPort("3306"), 127 ) 128 129 client.SetPrimaryKey("ID") // Change default primary key name 130 version := client.Version() // Display driver version 131 dbs, _ := client.ListDatabases(ctx) // List databases 132 133 userTable := client.Database("sqlike").Table("User") 134 135 // Drop Table 136 userTable.Drop(ctx) 137 138 // Migrate Table 139 userTable.Migrate(ctx, User{}) 140 141 // Truncate Table 142 userTable.Truncate(ctx) 143 144 // Insert one record 145 { 146 user := newUser() 147 if _, err := userTable.InsertOne(ctx, &user); err != nil { 148 panic(err) 149 } 150 } 151 152 // Insert multiple record 153 { 154 users := [...]User{ 155 newUser(), 156 newUser(), 157 newUser(), 158 } 159 if _, err := userTable.Insert(ctx, &users); err != nil { 160 panic(err) 161 } 162 } 163 164 // Find one record 165 { 166 user := User{} 167 err := userTable.FindOne(ctx, nil).Decode(&user) 168 if err != nil { 169 // `sqlike.ErrNoRows` is an alias of `sql.ErrNoRows` 170 if err != sqlike.ErrNoRows { // or you may check with sql.ErrNoRows 171 panic(err) 172 } 173 // record not exist 174 } 175 } 176 177 // Find multiple records 178 { 179 users := make([]User, 0) 180 result, err := userTable.Find( 181 ctx, 182 actions.Find(). 183 Where( 184 expr.Equal("ID", result.ID), 185 ). 186 OrderBy( 187 expr.Desc("UpdatedAt"), 188 ), 189 ) 190 if err != nil { 191 panic(err) 192 } 193 // map into the struct of slice 194 if err:= result.All(&users); err != nil { 195 panic(err) 196 } 197 } 198 199 // Update one record with all fields of struct 200 { 201 user.Name = `🤖 Hello World!` 202 if err := userTable.ModifyOne(ctx, &user); err != nil { 203 panic(err) 204 } 205 } 206 207 // Update one record with selected fields 208 { 209 userTable.UpdateOne( 210 ctx, 211 actions.UpdateOne(). 212 Where( 213 expr.Equal("ID", 100), 214 ).Set( 215 expr.ColumnValue("Name", "SianLoong"), 216 expr.ColumnValue("Email", "test@gmail.com"), 217 ), 218 options.UpdateOne().SetDebug(true), // debug the query 219 ) 220 } 221 222 { 223 limit := uint(10) 224 pg, err := userTable.Paginate( 225 ctx, 226 actions.Paginate(). 227 OrderBy( 228 expr.Desc("CreatedAt"), 229 ). 230 Limit(limit + 1), 231 options.Paginate().SetDebug(true), 232 ) 233 if err != nil { 234 panic(err) 235 } 236 237 for { 238 var users []User 239 if err := pg.All(&users); err != nil { 240 panic(err) 241 } 242 length := uint(len(users)) 243 if length == 0 { 244 break 245 } 246 cursor := users[length-1].ID 247 if err := pg.NextCursor(ctx, cursor); err != nil { 248 if err == sqlike.ErrInvalidCursor { 249 break 250 } 251 panic(err) 252 } 253 if length <= limit { 254 break 255 } 256 } 257 258 } 259 } 260 ``` 261 262 Inspired by [gorm](https://github.com/jinzhu/gorm), [mongodb-go-driver](https://github.com/mongodb/mongo-go-driver) and [sqlx](https://github.com/jmoiron/sqlx). 263 264 ## 🎉 Big Thanks To 265 266 Thanks to these awesome companies for their support of Open Source developers ❤ 267 268 [](https://github.com/open-source) 269 [](https://www.npmjs.com/) 270 271 ## 🧲 Adopters 272 273 <img src="https://camo.githubusercontent.com/2c975785e2902b4ca4d1a9e9fd4bd21b3e576395631f1dc7c49f170cdeb7f1f8/68747470733a2f2f61737365742e77657469782e6d792f696d616765732f6c6f676f2f77657469782e706e67" 274 width="120px" style="margin-right: 10px" /> 275 <img src="https://user-images.githubusercontent.com/28108597/188676138-59b5fa89-9788-48ce-9a12-bdcc378bb8b7.png" width="150px" style="margin-right: 10px" /> 276 <img src="https://user-images.githubusercontent.com/28108597/188675656-af6fb714-0460-40b3-8935-f0eeaa0886b1.png" width="130px" style="margin-right: 10px" /> 277 <img src="https://user-images.githubusercontent.com/28108597/188676512-59a6f5bd-5c57-4051-bbd9-9275402b415f.png" width="70px" /> 278 279 ## 📄 License 280 281 [MIT](https://github.com/RevenueMonster/sqlike/blob/main/LICENSE) 282 283 Copyright (c) 2019-present, SianLoong Lee 284 285 [](https://app.fossa.com/projects/git%2Bgithub.com%2Fsi3nloong%2Fsqlike?ref=badge_large)