Manage Your Go Database Like a Pro with GORM
Tags: go, database, mysql, backenddevelopment
Published: 2024-07-12 12:08:43

No matter how much we say SQL is better than NoSQL, we can all agree that writing SQL by hand is not much fun, especially where you have to do joins and the rest. The need to solve this annoying issue has led to the development of several different ORMs (Object Relational Mapper) that sit as an abstraction layer on top of our database. 

They provide developers a way to interact with data typically an SQL-compatible database using Objects and code instead of writing a bunch of SQL code. All programming languages have their ORM but in today's post we will take a brief look at [GORM](https://gorm.io/) an ORM that is developed for [Go or Go Lang](https://go.dev/) as it is commonly called hence we are going to consider the following talking points;

- Features of GORM
- Installation and setup
- Models
- Add data
- Read Data
- Update Data
- Delete Data

## Features of GORM

GORM is a Full-Featured and developer-friendly ORM built with out-of-the-box support for all types of associations, and hooks that you can listen to extend the behavior of your models further. GORM is written in Go and supports multiple SQL-based databases like [MySQL](https://www.mysql.com/), [SQL-Lite](https://www.sqlite.org/), [Postgre SQL](https://www.postgresql.org/), and [OracleDB](https://www.oracle.com/ng/database/). GORM has built-in support for Transactions, nested transactions, save points, and rollback to saved points. GORM allows us to run Batch Insert, FindInBatches, Find/Create with Map amongst others, let's see how we can install and setup GORM in a Go project.

## Installation and Setup

To install GORM you need to run the following command;

```bash
go get -u gorm.io/gorm
```
Next, you need to install a driver compatible with your SQL database and as such we will install the driver for MySQL with the following command. 

```bash
go get -u gorm.io/driver/mysql
```

Now we have GORM and a compatible SQL driver installed let's connect to a MySQL database.


```go
package main

import (
  "gorm.io/driver/mysql"
  "gorm.io/gorm"
)


func main () {
    db, err := gorm.Open(mysql.New(mysql.Config{
		DSN: "root@tcp(localhost:3306)/test?charset=utf8mb4&parseTime=true",
	}))

	if err != nil {
		panic("failed to connect database")
	}
}
```

## Models

Models serve as a way to map a struct in your Go program to a database table. Each Model corresponds to a table in your database, while the keys on the struct correspond to columns on that table. While the values for each key serve as a row in your database. Let's create a `User` struct that will correspond to a user table.

```go
import "gorm.io/gorm"

type User struct {
    gorm.Model,
    Name string `json:"name"`
    Email string `json:"email"`
}
```

You can observe that each key on the struct has a type and that type will correspond to the type for that particular value on our SQL table. We can use other types like `uint` and `bool`. We can do things like specify foreign keys and unique columns.


```go
import "gorm.io/gorm"

type User struct {
    gorm.Model,
    Name string `gorm:"type:varchar(20)" json:"name"`
    Email string `gorm:"unique;not null" json:"email"`
}
```

You can embed the `gorm.Model` struct into your model struct. This adds commonly used fields like:

`ID`: A unique identifier for each record (usually the primary key).
`CreatedAt`: Automatically set to the current time when a record is created.
`UpdatedAt`: Automatically updated whenever a record is modified.
`DeletedAt` (optional): Used for soft deletes (marking records as deleted without physically removing them).

## Add data

Let’s see how we can add data to our database using the model we just created.

```go

// cont’d

func CreateUser (db *gorm.DB, name string, email string) User {
    newUser := User{
        Name: name,
        Email: email,
    }
    result := db.Create(&newUser)
    if result.Error != nil {
        panic("error creating user")
    }
    return newUser
}

user := CreateUser(db, "sam","sam@gmail.Com")
```


The code above defines a function called `CreateUser` that takes a database connection `(db)`, a name `(name)`, and an email `(email)` as arguments and returns a User model. We can also create multiple users easily. 

```go

// cont’d

func CreateUsers(db *gorm.DB) []User {
    newUsers := []*User{
        {  Name: “jay”, Email: “jay@gmail.Com”  },
        {  Name: “joe”, Email: “joe@gmail.Com”  },
    }
    result := db.Create(&newUsers)
    if result.Error != nil {
        panic("error creating user")
    }
    return newUsers
}

users := CreateUsers(db)
```

## Read Data

Now we’ve created some data let’s see how we can read those data we just added.

```go
// get single record

func GetUser(db *gorm.DB, id uint) User {
    user = User{
        ID: id
    }

    result := db.First(&user)
    if result.Error != nil {
        panic("Error fetching data")
    }
    return user
}

user := GetUser(db, 1)

```

The code snippet above shows how to retrieve a single user, what if we are interested in getting multiple users?

```go
// get single record

func GetUsers(db *gorm.DB) []User {
    var users []User
    result := db.Find(&users)
    if result.Error != nil {
        panic("Error fetching data")
    }
    return users
}

users := GetUsers(db);

```

## Update Data

Now we’ve seen how to read our data let’s see how we can update our data.

```go
func UpdateUser(db *gorm.DB, id uint, name string) user {
    user := User{
        ID: id,
    }
    result := db.Find(&users).Update("name", name)
    if result.Error != nil {
        panic("Error fetching data")
    }
    return user
}

users := UpdateUser(db, 1, "sam");
```

If we ware interested in updating multiple columns for a particular data we can use the following approach;


```go
func UpdateUser(db *gorm.DB, id uint) user {
    user := User{
        Name: "phill"
        Email: "phill@gmail.com"
    }
    result := db.Where("id = ?", id).Updates(&user)
    if result.Error != nil {
        panic("Error fetching data")
    }
    return user
}

users := UpdateUser(db, 1);
```

## Delete Data  

Now let’s see how we can delete a record or multiple records from our database

```go

// delete single record

func DeleteUser(db *gorm.DB, id uint) {
    result := db.Delete(&User{}, id)
    if result.Error != nil {
        panic("Error fetching data")
     }
}

DeleteUser(db, 1)

// delete multiple records

func DeleteUsers(db *gorm.DB, ids []int{}) {
    result := db.Delete(&User{}, ids)
    if result.Error != nil {
        panic("Error fetching data")
     }
}
DeleteUsers(db, {1, 4, 10})
```

That’s going to be all for this one guys, I hope you found this useful leave your thoughts in the comments section below.