{"id":28725534,"url":"https://github.com/spacetent/gostructdb","last_synced_at":"2026-02-25T02:03:43.921Z","repository":{"id":293158458,"uuid":"983135919","full_name":"SpaceTent/GoStructDB","owner":"SpaceTent","description":"GoStructDB - ORM without so much of the ORM","archived":false,"fork":false,"pushed_at":"2025-09-09T09:46:59.000Z","size":66,"stargazers_count":0,"open_issues_count":4,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-22T16:51:41.427Z","etag":null,"topics":["database","go","golang","mysql","sqlite3"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/SpaceTent.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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,"zenodo":null}},"created_at":"2025-05-13T23:56:14.000Z","updated_at":"2025-08-21T20:14:10.000Z","dependencies_parsed_at":"2025-05-14T02:11:18.774Z","dependency_job_id":"2d36a0c3-8811-48e3-aecf-994db4a55d74","html_url":"https://github.com/SpaceTent/GoStructDB","commit_stats":null,"previous_names":["spacetent/gostructdb"],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/SpaceTent/GoStructDB","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SpaceTent%2FGoStructDB","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SpaceTent%2FGoStructDB/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SpaceTent%2FGoStructDB/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SpaceTent%2FGoStructDB/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SpaceTent","download_url":"https://codeload.github.com/SpaceTent/GoStructDB/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SpaceTent%2FGoStructDB/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29808929,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-24T22:43:48.403Z","status":"online","status_checked_at":"2026-02-25T02:00:07.329Z","response_time":61,"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","go","golang","mysql","sqlite3"],"created_at":"2025-06-15T12:00:54.718Z","updated_at":"2026-02-25T02:03:43.914Z","avatar_url":"https://github.com/SpaceTent.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Go StructDB - gsdb\n\nOpinionated Database Extraction Library for Go, It's got some ORM, but does not hide the SQL. \n\nWhile GORM is a popular choice, I believe strongly in developers understanding and writing SQL queries directly. \n\nSQL proficiency should be a fundamental skill for all developers. However, Go's verbose syntax can make database\ninteractions quite lengthy and repetitive.\n\nGSDB give you some ORM features but you still need to write SQL. \n\nGSDB works for me, as it provides direct access to run queries and access the results with simple \"record\" and \"field\" types.\nIt also provides Database to Struct transformations. If the struct has the extra \"db\" tags to define columns, primary keys read default conditions, GSDB will translate DB resources into Structs and provide methods such as .Save(); Making it easy to define a struct, load it with data and just call a DB.Save to save it to the database\n\nGSDB is designed to abstract a lot of the boilerplate DB code away from you. It makes writing CRUD applications easier, but, with that ease of use, comes trade-offs.\nGSDB uses reflection to determine many of the type conversions. It also holds complete record sets in memory. \nIf you need fast, high performance and efficient DB access, GSDB probably isn't for you.\n\nAnother thing I didn't like about SQL handling in Go, was making structs contain SQL data types such as sql.Null.\nTo me this seems lazy. It ties your struct to a database, increasing the boilerplate and scaffolding you need to work with the data. To me, the data is either strings or dates. Then the struct is either string or time.Time.\n\nI'm keen to keep primitives, as primitive as possible. \n\n### Connecting\n\nTo start a new DB connection, include GSDB library and declare a new connection\n\n```go\ngsdb.New(DataSource, StructuredLoggingHandler, context)\n```\n\n### Executing Queries\n\n```go\nlastInsertedID, rowsAffected, err := gsdb.DB.Execute(sqlQuery,parameters...)\n```\n### Making Insert and Update SQL\n\ngsdb provides 2 functions to make SQL statements from structs, DB.Insert and DB.Update. If you pass in any variable from a struct, the library will build the SQL necessary for an INSERT or UPDATE statement.\n\n```go\n    type InsertPerson struct {\n        Id      int       `db:\"column=id primarykey=yes table=Test\"`\n        Name    string    `db:\"column=name\"`\n        Dtadded time.Time `db:\"column=dtadded\"`\n        Status  int       `db:\"column=status\"`\n        Ignored int       `db:\"column=ignored omit=yes\"`\n    }\n\n\tentry := InsertPerson{\n\t\tName:    \"Test\",\n\t\tDtadded: time.Now(),\n\t\tStatus:  1,\n\t}\n\tsqlQuery, err := MySQL.DB.Insert(entry)\n\tif err != nil {\n\t\tl.Error(err.Error())\n\t\treturn\n\t}\n\t\n\tlastInsertedID, rowsAffected, err := MySQL.DB.Execute(sqlQuery)\n\tif err != nil {\n\t\tl.Error(err.Error())\n\t}\n\tl.Info(fmt.Sprintf(\"Item with ID %d was inserted. %d rows were affected\", lastInsertedID, rowsAffected))\n```\n\n### Hex conversion of strings\n\nAll strings are converted to HEX. This ensures even the most challenging characters are written to the DB as it also makes SQL injections attacks difficult.  \n\n```sql\nINSERT INTO Test(name,dtadded,status) VALUES (X'54657374','2025-12-25 15:29:25',1);\n```\n\n### QueryStruct\n\nQueryStruct will return a slice of type T containing all the data.\n\n### QuerySingleStruct\n\nThis works the same as QueryStruct, but returns T, not a slice of type T. \nWhen returning datetimes from the database, there are some extra options to determine how you can view NULL or EMPTY dates in the database. \"readdefault=now\" or \"readdefault=zero\"\n\nIn early versions of GSDB, if a date field in the database was NULL, the library returned time.Now() instead. That behavior wasn’t really correct, but some applications had already built business logic around the idea that a NULL date meant “current time.” To preserve compatibility, GSDB kept this as the default.\n\nHowever, the recommended approach is to use the tag readdefault=zero, which tells GSDB to return the Go “zero time” (0001-01-01 00:00:00) for NULL dates. This is usually the proper way to represent missing or unset timestamps.\n\n### Save\n\nIf the primary key is zero, then an Insert is executed, otherwise it's an Update. \n\n```go\n type InsertPerson struct {\n        Id      int       `db:\"column=id primarykey=yes table=Test\"`\n        Name    string    `db:\"column=name\"`\n        Dtadded time.Time `db:\"column=dtadded\"`\n        Status  int       `db:\"column=status\"`\n        Ignored int       `db:\"column=ignored omit=yes\"`\n    }\n\n\tentry := InsertPerson{\n\t\tName:    \"Test\",\n\t\tDtadded: time.Now(),\n\t\tStatus:  1,\n\t}\n    \n    gsdb.New(DataSource, StructuredLoggingHandler, context)\n    \n    gsdb.Save(entry,0)\n    \n ```   \n\n### Counters\n\nYou can start a counter anywhere in your call code, and then call the getCounter functions to see how many SQL statements have happened since that counter was started. \n\nThis is experimental at this stage. \n\n```go\n\tMySQL.DB.StartCounter(\"test\")\n\t\n\t// Loads of DB calls \n\t\n\tcount := MySQL.DB.GetCounter(\"test)\n```\n\n### ColumnWarnings\n\nIf the struct doesn't match the SQL returned or generated.  The library will spit out a warning that there is a mismatch, and then ignore it.  You can turn of this warning with \n\n```go\n    MySQL.ColumnWarnings = true\n    P, _ := MySQL.QuerySingleStruct[InsertPerson](\"select * from Test WHERE ID = ?\", lastInsertedID)\n```\nIf there are any fields defined in InsertPerson that don't match the record set, a warning is raised. \n\n\n### ShowSQL\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fspacetent%2Fgostructdb","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fspacetent%2Fgostructdb","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fspacetent%2Fgostructdb/lists"}