Accessing a relational database
This tutorial introduces the basics of accessing a relational database with
Go and the database/sql package in its standard library.
You'll get the most out of this tutorial if you have a basic familiarity with Go and its tooling. If this is your first exposure to Go, please see Tutorial: Get started with Go for a quick introduction.
The database/sql package you'll
be using includes types and functions for connecting to databases, executing
transactions, canceling an operation in progress, and more. For more details
on using the package, see
Accessing databases.
In this tutorial, you'll create a database, then write code to access the database. Your example project will be a repository of data about vintage jazz records.
In this tutorial, you'll progress through the following sections:
- Create a folder for your code.
- Set up a database.
- Import the database driver.
- Get a database handle and connect.
- Query for multiple rows.
- Query for a single row.
- Add data.
Prerequisites
- An installation of the MySQL relational database management system (DBMS).
- An installation of Go. For installation instructions, see Installing Go.
- A tool to edit your code. Any text editor you have will work fine.
- A command terminal. Go works well using any terminal on Linux and Mac, and on PowerShell or cmd in Windows.
Create a folder for your code
To begin, create a folder for the code you'll write.
-
Open a command prompt and change to your home directory.
On Linux or Mac:
On Windows:
For the rest of the tutorial we will show a $ as the prompt. The commands we use will work on Windows too.
-
From the command prompt, create a directory for your code called data-access.
-
Create a module in which you can manage dependencies you will add during this tutorial.
Run the
go mod initcommand, giving it your new code's module path.This command creates a go.mod file in which dependencies you add will be listed for tracking. For more, be sure to see Managing dependencies.
Note: In actual development, you'd specify a module path that's more specific to your own needs. For more, see Managing dependencies.
Next, you'll create a database.
Set up a database
In this step, you'll create the database you'll be working with. You'll use the CLI for the DBMS itself to create the database and table, as well as to add data.
You'll be creating a database with data about vintage jazz recordings on vinyl.
The code here uses the MySQL CLI, but most DBMSes have their own CLI with similar features.
-
Open a new command prompt.
-
At the command line, log into your DBMS, as in the following example for MySQL.
-
At the
mysqlcommand prompt, create a database. -
Change to the database you just created so you can add tables.
-
In your text editor, in the data-access folder, create a file called create-tables.sql to hold SQL script for adding tables.
-
Into the file, paste the following SQL code, then save the file.
In this SQL code, you:
-
Delete (drop) a table called
album. Executing this command first makes it easier for you to re-run the script later if you want to start over with the table. -
Create an
albumtable with four columns:title,artist, andprice. Each row'sidvalue is created automatically by the DBMS. -
Add four rows with values.
-
-
From the
mysqlcommand prompt, run the script you just created.You'll use the
sourcecommand in the following form: -
At your DBMS command prompt, use a
SELECTstatement to verify you've successfully created the table with data.
Next, you'll write some Go code to connect so you can query.
Find and import a database driver
Now that you've got a database with some data, get your Go code started.
Locate and import a database driver that will translate requests you make
through functions in the database/sql package into requests the database
understands.
-
In your browser, visit the SQLDrivers wiki page to identify a driver you can use.
Use the list on the page to identify the driver you'll use. For accessing MySQL in this tutorial, you'll use Go-MySQL-Driver.
-
Note the package name for the driver -- here,
github.com/go-sql-driver/mysql. -
Using your text editor, create a file in which to write your Go code and save the file as main.go in the data-access directory you created earlier.
-
Into main.go, paste the following code to import the driver package.
In this code, you:
-
Add your code to a
mainpackage so you can execute it independently. -
Import the MySQL driver
github.com/go-sql-driver/mysql.
-
With the driver imported, you'll start writing code to access the database.
Get a database handle and connect
Now write some Go code that gives you database access with a database handle.
You'll use a pointer to an sql.DB struct, which represents access to a
specific database.
Write the code
-
Into main.go, beneath the
importcode you just added, paste the following Go code to create a database handle.In this code, you:
-
Declare a
dbvariable of type*sql.DB. This is your database handle.Making
dba global variable simplifies this example. In production, you'd avoid the global variable, such as by passing the variable to functions that need it or by wrapping it in a struct. -
Use the MySQL driver's
Config-- and the type'sFormatDSN-– to collect connection properties and format them into a DSN for a connection string.The
Configstruct makes for code that's easier to read than a connection string would be. -
Call
sql.Opento initialize thedbvariable, passing the return value ofFormatDSN. -
Check for an error from
sql.Open. It could fail if, for example, your database connection specifics weren't well-formed.To simplify the code, you're calling
log.Fatalto end execution and print the error to the console. In production code, you'll want to handle errors in a more graceful way. -
Call
DB.Pingto confirm that connecting to the database works. At run time,sql.Openmight not immediately connect, depending on the driver. You're usingPinghere to confirm that thedatabase/sqlpackage can connect when it needs to. -
Check for an error from
Ping, in case the connection failed. -
Print a message if
Pingconnects successfully.
-
-
Near the top of the main.go file, just beneath the package declaration, import the packages you'll need to support the code you've just written.
The top of the file should now look like this:
-
Save main.go.
Run the code
-
Begin tracking the MySQL driver module as a dependency.
Use the
go getto add the github.com/go-sql-driver/mysql module as a dependency for your own module. Use a dot argument to mean "get dependencies for code in the current directory."Go downloaded this dependency because you added it to the
importdeclaration in the previous step. For more about dependency tracking, see Adding a dependency. -
From the command prompt, set the
DBUSERandDBPASSenvironment variables for use by the Go program.On Linux or Mac:
On Windows:
-
From the command line in the directory containing main.go, run the code by typing
go runwith a dot argument to mean "run the package in the current directory."
You can connect! Next, you'll query for some data.
Query for multiple rows
In this section, you'll use Go to execute an SQL query designed to return multiple rows.
For SQL statements that might return multiple rows, you use the Query method
from the database/sql package, then loop through the rows it returns. (You'll
learn how to query for a single row later, in the section
Query for a single row.)
Write the code
-
Into main.go, immediately above
func main, paste the following definition of anAlbumstruct. You'll use this to hold row data returned from the query. -
Beneath
func main, paste the followingalbumsByArtistfunction to query the database.In this code, you:
-
Declare an
albumsslice of theAlbumtype you defined. This will hold data from returned rows. Struct field names and types correspond to database column names and types. -
Use
DB.Queryto execute aSELECTstatement to query for albums with the specified artist name.Query's first parameter is the SQL statement. After the parameter, you can pass zero or more parameters of any type. These provide a place for you to specify the values for parameters in your SQL statement. By separating the SQL statement from parameter values (rather than concatenating them with, say,fmt.Sprintf), you enable thedatabase/sqlpackage to send the values separate from the SQL text, removing any SQL injection risk. -
Defer closing
rowsso that any resources it holds will be released when the function exits. -
Loop through the returned rows, using
Rows.Scanto assign each row’s column values toAlbumstruct fields.Scantakes a list of pointers to Go values, where the column values will be written. Here, you pass pointers to fields in thealbvariable, created using the&operator.Scanwrites through the pointers to update the struct fields. -
Inside the loop, check for an error from scanning column values into the struct fields.
-
Inside the loop, append the new
albto thealbumsslice. -
After the loop, check for an error from the overall query, using
rows.Err. Note that if the query itself fails, checking for an error here is the only way to find out that the results are incomplete.
-
-
Update your
mainfunction to callalbumsByArtist.To the end of
func main, add the following code.In the new code, you now:
-
Call the
albumsByArtistfunction you added, assigning its return value to a newalbumsvariable. -
Print the result.
-
Run the code
From the command line in the directory containing main.go, run the code.
Next, you'll query for a single row.
Query for a single row
In this section, you'll use Go to query for a single row in the database.
For SQL statements you know will return at most a single row, you can use
QueryRow, which is simpler than using a Query loop.
Write the code
-
Beneath
albumsByArtist, paste the followingalbumByIDfunction.In this code, you:
-
Use
DB.QueryRowto execute aSELECTstatement to query for an album with the specified ID.It returns an
sql.Row. To simplify the calling code (your code!),QueryRowdoesn't return an error. Instead, it arranges to return any query error (such assql.ErrNoRows) fromRows.Scanlater. -
Use
Row.Scanto copy column values into struct fields. -
Check for an error from
Scan.The special error
sql.ErrNoRowsindicates that the query returned no rows. Typically that error is worth replacing with more specific text, such as “no such album” here.
-
-
Update
mainto callalbumByID.To the end of
func main, add the following code.In the new code, you now:
-
Call the
albumByIDfunction you added. -
Print the album ID returned.
-
Run the code
From the command line in the directory containing main.go, run the code.
Next, you'll add an album to the database.
Add data
In this section, you'll use Go to execute an SQL INSERT statement to add a
new row to the database.
You’ve seen how to use Query and QueryRow with SQL statements that
return data. To execute SQL statements that don't return data, you use Exec.
Write the code
-
Beneath
albumByID, paste the followingaddAlbumfunction to insert a new album in the database, then save the main.go.In this code, you:
-
Use
DB.Execto execute anINSERTstatement.Like
Query,Exectakes an SQL statement followed by parameter values for the SQL statement. -
Check for an error from the attempt to
INSERT. -
Retrieve the ID of the inserted database row using
Result.LastInsertId. -
Check for an error from the attempt to retrieve the ID.
-
-
Update
mainto call the newaddAlbumfunction.To the end of
func main, add the following code.In the new code, you now:
- Call
addAlbumwith a new album, assigning the ID of the album you're adding to analbIDvariable.
- Call
Run the code
From the command line in the directory containing main.go, run the code.
Conclusion
Congratulations! You've just used Go to perform simple actions with a relational database.
Suggested next topics:
-
Take a look at the data access guide, which includes more information about the subjects only touched on here.
-
If you're new to Go, you'll find useful best practices described in Effective Go and How to write Go code.
-
The Go Tour is a great step-by-step introduction to Go fundamentals.
Completed code
This section contains the code for the application you build with this tutorial.