i'm getting Stuck as TRY my first CRUD with MySQL (5.7) using GO lang (1.18). CREATE & READ SQL not return the error, but in UPDATE & DELETE return the error. I was searching and looks like about bad or wrong configuration for connection...but i dont know how to trace it.
Driver MySQL: github.com/go-sql-driver/mysql v1.6.0
My Repo: https://github.com/ramadoiranedar/GO_REST_API
The Error is:
[mysql] 2022/09/22 00:04:44 connection.go:299: invalid connection
driver: bad connection
And i have functions SQL look likes this:
CONNECTION
func NewDB() *sql.DB {
db, err := sql.Open("mysql", "MY_USERNAME:MY_PASSWORD#tcp(localhost:3306)/go_restful_api")
helper.PanicIfError(err)
db.SetMaxIdleConns(5)
db.SetMaxIdleConns(20)
db.SetConnMaxLifetime(60 * time.Minute)
db.SetConnMaxIdleTime(10 * time.Minute)
return db
}
UPDATE
func (repository *CategoryRepositoryImpl) Update(ctx context.Context, tx *sql.Tx, category domain.Category) domain.Category {
SQL := "update category set name = ? where id = ?"
_, err := tx.ExecContext(ctx, SQL, category.Name, category.Id)
helper.PanicIfError(err)
return category
}
DELETE
func (repository *CategoryRepositoryImpl) Delete(ctx context.Context, tx *sql.Tx, category domain.Category) {
SQL := "delete from category where id = ?"
_, err := tx.ExecContext(ctx, SQL, category.Name, category.Id)
helper.PanicIfError(err)
}
CREATE
func (repository *CategoryRepositoryImpl) Create(ctx context.Context, tx *sql.Tx, category domain.Category) domain.Category {
SQL := "insert into category (name) values (?)"
result, err := tx.ExecContext(ctx, SQL, category.Name)
helper.PanicIfError(err)
id, err := result.LastInsertId()
if err != nil {
panic(err)
}
category.Id = int(id)
return category
}
READ
func (repository *CategoryRepositoryImpl) FindAll(ctx context.Context, tx *sql.Tx) []domain.Category {
SQL := "select id, name from category"
rows, err := tx.QueryContext(ctx, SQL)
helper.PanicIfError(err)
var categories []domain.Category
for rows.Next() {
category := domain.Category{}
err := rows.Scan(
&category.Id,
&category.Name,
)
helper.PanicIfError(err)
categories = append(categories, category)
}
return categories
}
func (repository *CategoryRepositoryImpl) FindById(ctx context.Context, tx *sql.Tx, categoryId int) (domain.Category, error) {
SQL := "select id, name from category where id = ?"
rows, err := tx.QueryContext(ctx, SQL, categoryId)
helper.PanicIfError(err)
category := domain.Category{}
if rows.Next() {
err := rows.Scan(&category.Id, &category.Name)
helper.PanicIfError(err)
return category, nil
} else {
return category, errors.New("CATEGORY IS NOT FOUND")
}
}
Long story short: you are using database transactions where you should not.
Keep them for group of writing operations that must succeed.
I would suggest you to redefine your repository and its implementation. The whole idea of splitting layers is to be independant of a repository implementation and to be able to switch from mysql to mongo to postgres, or w/e.
//category_controller.go
type CategoryRepository interface {
Create(ctx context.Context, category domain.Category) domain.Category
Update(ctx context.Context, category domain.Category) domain.Category
Delete(ctx context.Context, category domain.Category)
FindById(ctx context.Context, categoryId int) (domain.Category, error)
FindAll(ctx context.Context) []domain.Category
}
//category_repository_impl.go
type CategoryRepositoryImpl struct {
db *sql.DB
}
func NewCategoryRepository(db *sql.DB) CategoryRepository {
return &CategoryRepositoryImpl{db: db}
}
Please see following gists with modified version of your code files:
https://gist.github.com/audrenbdb/94660f707a206d385c42f64ceb93a4aa
Related
i'm getting Stuck as TRY my first CRUD with MySQL (5.7) using GO lang (1.18). CREATE & READ SQL not return the error, but in UPDATE & DELETE return the error. I was searching and looks like about bad or wrong configuration for connection...but i dont know how to trace it.
Driver MySQL: github.com/go-sql-driver/mysql v1.6.0
My Repo: https://github.com/ramadoiranedar/GO_REST_API
The Error is:
[mysql] 2022/09/22 00:04:44 connection.go:299: invalid connection
driver: bad connection
And i have functions SQL look likes this:
CONNECTION
func NewDB() *sql.DB {
db, err := sql.Open("mysql", "MY_USERNAME:MY_PASSWORD#tcp(localhost:3306)/go_restful_api")
helper.PanicIfError(err)
db.SetMaxIdleConns(5)
db.SetMaxIdleConns(20)
db.SetConnMaxLifetime(60 * time.Minute)
db.SetConnMaxIdleTime(10 * time.Minute)
return db
}
UPDATE
func (repository *CategoryRepositoryImpl) Update(ctx context.Context, tx *sql.Tx, category domain.Category) domain.Category {
SQL := "update category set name = ? where id = ?"
_, err := tx.ExecContext(ctx, SQL, category.Name, category.Id)
helper.PanicIfError(err)
return category
}
DELETE
func (repository *CategoryRepositoryImpl) Delete(ctx context.Context, tx *sql.Tx, category domain.Category) {
SQL := "delete from category where id = ?"
_, err := tx.ExecContext(ctx, SQL, category.Name, category.Id)
helper.PanicIfError(err)
}
CREATE
func (repository *CategoryRepositoryImpl) Create(ctx context.Context, tx *sql.Tx, category domain.Category) domain.Category {
SQL := "insert into category (name) values (?)"
result, err := tx.ExecContext(ctx, SQL, category.Name)
helper.PanicIfError(err)
id, err := result.LastInsertId()
if err != nil {
panic(err)
}
category.Id = int(id)
return category
}
READ
func (repository *CategoryRepositoryImpl) FindAll(ctx context.Context, tx *sql.Tx) []domain.Category {
SQL := "select id, name from category"
rows, err := tx.QueryContext(ctx, SQL)
helper.PanicIfError(err)
var categories []domain.Category
for rows.Next() {
category := domain.Category{}
err := rows.Scan(
&category.Id,
&category.Name,
)
helper.PanicIfError(err)
categories = append(categories, category)
}
return categories
}
func (repository *CategoryRepositoryImpl) FindById(ctx context.Context, tx *sql.Tx, categoryId int) (domain.Category, error) {
SQL := "select id, name from category where id = ?"
rows, err := tx.QueryContext(ctx, SQL, categoryId)
helper.PanicIfError(err)
category := domain.Category{}
if rows.Next() {
err := rows.Scan(&category.Id, &category.Name)
helper.PanicIfError(err)
return category, nil
} else {
return category, errors.New("CATEGORY IS NOT FOUND")
}
}
Long story short: you are using database transactions where you should not.
Keep them for group of writing operations that must succeed.
I would suggest you to redefine your repository and its implementation. The whole idea of splitting layers is to be independant of a repository implementation and to be able to switch from mysql to mongo to postgres, or w/e.
//category_controller.go
type CategoryRepository interface {
Create(ctx context.Context, category domain.Category) domain.Category
Update(ctx context.Context, category domain.Category) domain.Category
Delete(ctx context.Context, category domain.Category)
FindById(ctx context.Context, categoryId int) (domain.Category, error)
FindAll(ctx context.Context) []domain.Category
}
//category_repository_impl.go
type CategoryRepositoryImpl struct {
db *sql.DB
}
func NewCategoryRepository(db *sql.DB) CategoryRepository {
return &CategoryRepositoryImpl{db: db}
}
Please see following gists with modified version of your code files:
https://gist.github.com/audrenbdb/94660f707a206d385c42f64ceb93a4aa
I want to make a unit test 100% coverage but many commands or checks are missed in the unit testing, how do I fix it so that it can be 100% coverage
func (db *Database) SaveBook(id string, url string, username string, email string, password string, data string) error {
sqlStatement := "INSERT INTO book (id, cerator,name_book) VALUES (?, ?, ?)"
stmt, err := db.DB.Prepare(sqlStatement)
if err != nil {
return err
}
result, errExec := stmt.Exec(id, url, username, email, password, data)
if errExec != nil {
return errExec
}
rows, err := result.RowsAffected()
if err != nil {
return err
}
if rows != 1 {
return err
}
return nil
}
and for the unit testing example I use this. what's wrong with this
func TestSavetWsSuccesss(t *testing.T) {
db, mock := NewMock()
repo := data.NewDatabase(db)
query := "INSERT INTO book (id, cerator,name_book) VALUES (?, ?, ?)"
prep := mock.ExpectPrepare(regexp.QuoteMeta(query))
prep.ExpectExec().WithArgs(id,creator,name).WillReturnError(errors.New("sql error"))
}
For all of my GET requests, I'm getting an Internal Server Error 500. I'm not sure what I'm doing wrong, so maybe there's something that I don't understand or am looking over and need a second pair of eyes. Any help/hints are appreciated. My code is below:
/**
* Return all Users and their respective rows from users table
**/
func (user *User) GetAll() ([]User, *errors.RestErr) {
var (
currUser User
users []User
)
//queryGetAllUsers = "select id, first_name, last_name, email, username, password, created_on, updated_on FROM users;
rows, err := users_db.Client.Query(queryGetAllUsers)
if err != nil {
return nil, errors.NewInternalServerError(err.Error())
}
// loop through addresses of users returned from database and append column values to collection
for rows.Next() {
if err := rows.Scan(&user.ID, &user.FirstName, &user.LastName, &user.Email, &user.Username, &user.Password, &user.CreatedOn, &user.UpdatedOn); err != nil {
return nil, mysql_utils.ParseError(err)
}
users = append(users, currUser)
}
defer rows.Close()
if len(users) == 0 {
return nil, errors.NewNotFoundError("No users in table")
}
return users, nil
}
I'm trying to build a query as follows, here I'm using gorp.
func GetAgregatedDownloadsFromTo(constrains Constrains) [] dao.NameValue {
dbMap := utils.GetDBConnection("radsummary");
defer dbMap.Db.Close()
var totalDailyDownloads[] NameValue
query := "SELECT SUM(outputoctets) as value ,date as name FROM dailyacct where date >= ? AND date < ? "
if len(constrains.LocationGroups) > 0 {
query = query + " AND calledstationid=? "
for i := 1; i< len(constrains.LocationGroups); i++ {
query = query + " OR calledstationid=? "
}
query = query + " group by date"
print(query)
_, err := dbMap.Select(&totalDailyDownloads, query, constrains.From, constrains.To, constrains.LocationGroups...)
if err != nil {
panic(err.Error()) // proper error handling instead of panic
}
}
return totalDailyDownloads
}
type Constrains struct {
From string `json:"from"`
To string `json:"to"`
LocationGroups []string `json:"locationgroups"`
}
Query construction happens based on the length of constrains.LocationGroups. Trouble I'm having is passing the variable number of args to the Select query once I give constrains.LocationGroups... as select query parameters it throws a compiler error too many arguments in call to dbMap.Select
Is it possible to achieve this kind of requirement?. Appreciate your input.
Found an answer based on Pass string slice to variadic empty interface parameter
Below is the updated code to achieve the task
func GetAgregatedDownloadsFromTo(constrains dao.Constrains) [] dao.NameValue {
dbMap := utils.GetDBConnection("radsummary");
defer dbMap.Db.Close()
var totalDailyDownloads[] dao.NameValue
query := "SELECT SUM(outputoctets) as value ,date as name FROM dailyacct where date >= ? AND date < ? "
if len(constrains.LocationGroups) > 0 {
args := make([]interface{}, len(constrains.LocationGroups)+2)
args[0] = constrains.From
args[1] = constrains.To
for index, value := range constrains.LocationGroups { args[index+2] = value }
query = query + " AND calledstationid=? "
for i := 1; i< len(constrains.LocationGroups); i++ {
query = query + " OR calledstationid=? "
}
query = query + " group by date"
print(query)
_, err := dbMap.Select(&totalDailyDownloads, query, args...)
if err != nil {
panic(err.Error()) // proper error handling instead of panic
}
}
return totalDailyDownloads
}
Here I had to convert the string slice to an interface slice.
I've wrote an function (In Go, of course) that inserting map[string]interface{} to mysql via this library.
Explanation of the code below:
The functions receives string called table and map[string]interface{} called data.
I separate the data to keys (variable called columns) and values (variable called values).
I generate from the columns variable called column_text that will look like this: first_name, last_name, birth_day, date_added
I generate from the values variable called variable_text that will look like this: ?, ?, ?, ?
I open mysql connection: db, err := sql.Open("mysql", "user:pass#/database")
I create prepared statement: stmt, err := db.Prepare("INSERT INTO " + table + " ( " + columns_text + " ) VALUES ( " + values_text + " )")
I execute the prepare statement. but I have a problem. the number of the columns and values changes all the time, and the stmt.Exec() command can't receive an slice (Array) like this: stmt.Exec(values), only the values alone like this: stmt.Exec(values[0], values[1], values[2]...)
The question:
I'm coming from PHP, where PDO::Statement could receive an array when executing.
How can I execute the statement with the slice (Array)? (If I can do it with different library, please write the name of the library and how to use it, thank you!)
The code:
func insertToDB(table string, data map[string]interface{}) {
columns := make([]interface{}, 0, len(data))
values := make([]interface{}, 0, len(data))
for key, _ := range data {
columns = append(columns, key)
values = append(values, data[key])
}
columns_text := ""
i := 0
of := len(data)
for i < of {
column := columns[i].(string)
if i == 0 {
columns_text = column
} else {
columns_text = columns_text + ", " + column
}
i++
}
fmt.Println(columns_text + " = " + table)
values_text := ""
i = 0
for i < of {
if i == 0 {
values_text = "?"
} else {
values_text = values_text + ", ?"
}
i++
}
fmt.Println(values_text)
fmt.Println(values)
fmt.Println(data)
db, err := sql.Open("mysql", "root:root#/bacafe")
if err != nil {
return -1, err
}
defer db.Close()
stmtIns, err := db.Prepare("INSERT INTO " + table + " ( " + columns_text + " ) VALUES ( " + values_text + " )")
if err != nil {
return -1, err
}
defer stmtIns.Close() // Close the statement when we leave main() / the program terminates
result, err := stmtIns.Exec(values...)
if err != nil {
return -1, err
} else {
insertedID, err := result.LastInsertId()
if err != nil {
return -1, err
} else {
return int(insertedID), nil
}
}
}
EDIT: I've edited the function above and it works perfectly now.
Thank you!
You are on the right track however Stmt.Exec takes args ...interface{}, so for your specific example you need to change 2 things:
......
values := make([]interface{}, 0, len(data))
......
//adding ... expands the values, think of it like func.apply(this, array-of-values) in
// javascript, in a way.
_, err = stmtIns.Exec(values...)