You can find a lot of useful applications to make, modify and interact with a database on the Internet: Microsoft SQL Server, MySQL, SQLite, Oracle, and so on. You can also use Go for that. In this topic, you'll be introduced to GORM, a full-featured object-relation-mapping (ORM) library for Go.
In particular, you'll learn how to install GORM, connect to a SQLite database using GORM, declare a GORM model, and retrieve data from a database using GORM.
What is GORM?
In simple terms, GORM is an open-source ORM library that allows you to match and synchronize the data flow between two different types of systems — relational databases and the Go programming language.
Even though Go is not a pure object-oriented language, it has the struct data type, methods, and interfaces that allow an object-oriented programming style. Furthermore, these features will enable you to use GORM to create models, which are struct types representing a specific database table.
map[string]interface{}. However, GORM's preferred convention is to use model structs to represent tables.Finally, GORM provides official support for SQLite, MySQL, PostgreSQL, and Microsoft SQL Server databases. It also allows you to create a custom database driver if it implements the Dialector interface. An example of a custom database driver is the GORM Oracle Driver.
How to install GORM
Before you can use GORM in your Go project, you'll need to initialize Go modules and install the required GORM packages.
import the gorm package and the sqlite database driver to connect to the chinook database.After you've downloaded the example-gorm project files, open the main.go file and take a look at the import statement:
import (
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
When working with GORM, you'll need to import the base GORM package "gorm.io/gorm" — it contains all the dependencies required to create models and perform queries to the database. You'll also need to import a database driver that allows you to connect to the database; in this case "gorm.io/driver/sqlite" to connect to a SQLite DB.
The next step is to initialize Go modules for the example-gorm project and then execute go mod tidy to install the gorm and sqlite packages:
$ go mod init example-gorm
$ go mod tidy
Good going! You're set to start using GORM to interact with the chinook database.
Declaring models
GORM allows you to declare a new model and then create a new table using it or declare a model and map it to an existing database table. In this introductory topic, you'll only look at the latter: you'll map the artists table of the chinook database to the Artist model struct within main.go.
PascalCase naming conventions, with the first letter of each word capitalized. Additionally, GORM requires pluralized table names, for example artists, and expects both table names and table columns to follow snake_case naming conventions. If the table columns do not have snake_case names, you can use the gorm:"column:x" struct tag to override the snake_case naming convention requirement.The artists table has two columns, ArtistId and Name, so you can add the ArtistId and Name fields respectively to the Artist model struct. Since the artists table column names do not follow snake_case naming conventions, you'll need to use the column struct tag to override the snake_case name requirement:
type Artist struct {
ArtistId uint `gorm:"column:artistid"`
Name string
}
Take notice that if you didn't add the `gorm:"column:artistid"` struct tag to the ArtistId field, GORM would then use the column ArtistId as artist_id and since the artist_id column doesn't exist; you would get the no such column: error when executing queries:
2022/06/27 20:51:46 ... no such column: artists.artist_id
[0.000ms] [rows:0] SELECT * FROM artists ORDER BY artists.artist_id LIMIT 1
Now you might be wondering why it is not required to add the `gorm:"column:name"` struct tag to the Name field. Since Name is a simple field name and not a compound field name like ArtistId, it doesn't require any additional struct tags because it will just be used as name in lowercase (without any _ underscores).
Naming conventions
When working with an ORM library such as GORM, certain naming conventions can be used interchangeably between ORM and database elements.
|
GORM Naming Convention |
SQL Naming Convention |
|---|---|
| Model(s) | Table(s) |
| Field(s) | Column(s) |
| Object(s) | Record(s) |
To better understand the interchangeable naming conventions, let's take a look at some examples:
- When referring to the
Artistmodel, it is the same as referring to theartiststable; - When referring to the
Namefield, it is the same as referring to thenamecolumn; - When referring to an object in the
Artistmodel, it is the same as referring to a record in theartiststable.
Connecting to an existing database
Now that you've learned about interchangeable naming conventions and declared the Artist model struct, the next step is to use the gorm.Open() function to connect to the chinook database:
...
func main() {
db, err := gorm.Open(sqlite.Open("chinook.db"), &gorm.Config{})
if err != nil {
log.Fatal(err)
}
}
The gorm.Open() function takes two arguments: a Dialector interface that the sqlite database driver sqlite.Open() function returns, and an Option interface, which, by GORM conventions, is a pointer to the gorm.Config struct.
In short, the gorm.Config struct has a series of fields that allow you to enable or disable configuration options for the GORM connection.
Retrieving records from the database
Now that you have connected to the chinook database, it is time to perform your first query and retrieve data from the artists table. GORM provides three simple methods to retrieve a single record from the database:
-
db.First()returns the first record, ordered by the primary key; -
db.Take()returns one record with no specified order; -
db.Last()returns the last record, ordered by the primary key in descending order.
Now go ahead and use these methods within your Go program:
...
func main() {
... // Connect to the chinook database using gorm.Open()
var firstArtist, takeArtist, lastArtist Artist
db.First(&firstArtist) // SELECT * FROM artists ORDER BY artistid LIMIT 1;
db.Take(&takeArtist) // SELECT * FROM artists LIMIT 1;
db.Last(&lastArtist) // SELECT * FROM artists ORDER BY artistid DESC LIMIT 1;
fmt.Println(firstArtist) // {1 AC/DC}
fmt.Println(takeArtist) // {1 AC/DC}
fmt.Println(lastArtist) // {275 Philip Glass Ensemble}
}
As you can see, db.First(), db.Take(), and db.Last() represent different SQL query statements that retrieve a single record from the artists table.
And to retrieve all records from the artists table, you can use the db.Find() method:
...
func main() {
... // Connect to the chinook database using gorm.Open()
var artists []Artist
db.Find(&artists) // SELECT * FROM artists;
for _, artist := range artists {
fmt.Println(artist)
}
}
// Output:
// {1 AC/DC}
// {2 Accept}
// {3 Aerosmith}
// ...Conclusion
This topic has introduced you to the basics of GORM. Now you can do many things:
- Install GORM into your Go project and connect to an existing SQLite, MySQL, PostgreSQL, or SQL Server database;
- Declare a model struct and map it to a table from an existing database;
- Retrieve a single record or all records from a table;
If you want to learn more, please look at GORM's official docs. Now, go ahead and solve some theory and coding tasks!