面试题答案
一键面试设计场景
假设我们正在编写一个处理文件读取和数据库操作的Go程序。程序需要从文件中读取数据,解析数据后插入到数据库中。这个过程涉及多层嵌套函数调用,并且可能会出现文件读取错误、数据解析错误以及数据库插入错误。
代码实现
package main
import (
"database/sql"
"fmt"
"os"
_ "github.com/lib/pq" // 这里假设使用PostgreSQL,实际根据需要更改
)
// 模拟数据库连接
func connectDB() (*sql.DB, error) {
// 实际连接字符串根据数据库实际情况填写
db, err := sql.Open("postgres", "user=postgres dbname=mydb sslmode=disable")
if err != nil {
return nil, err
}
// 尝试ping数据库,确保连接可用
err = db.Ping()
if err != nil {
return nil, err
}
return db, nil
}
// 从文件读取数据
func readFile(filePath string) ([]byte, error) {
data, err := os.ReadFile(filePath)
if err != nil {
return nil, err
}
return data, nil
}
// 解析数据
func parseData(data []byte) ([]string, error) {
// 简单示例,实际可能更复杂
if len(data) == 0 {
return nil, fmt.Errorf("data is empty")
}
return []string{string(data)}, nil
}
// 插入数据到数据库
func insertData(db *sql.DB, data []string) error {
tx, err := db.Begin()
if err != nil {
return err
}
stmt, err := tx.Prepare("INSERT INTO my_table (column1) VALUES ($1)")
if err != nil {
tx.Rollback()
return err
}
defer stmt.Close()
for _, value := range data {
_, err = stmt.Exec(value)
if err != nil {
tx.Rollback()
return err
}
}
err = tx.Commit()
if err != nil {
return err
}
return nil
}
func main() {
filePath := "test.txt"
db, err := connectDB()
if err != nil {
fmt.Println("Failed to connect to database:", err)
return
}
defer db.Close()
data, err := readFile(filePath)
if err != nil {
fmt.Println("Failed to read file:", err)
return
}
parsedData, err := parseData(data)
if err != nil {
fmt.Println("Failed to parse data:", err)
return
}
err = insertData(db, parsedData)
if err != nil {
fmt.Println("Failed to insert data into database:", err)
return
}
fmt.Println("Data inserted successfully")
}
解释
- defer用于资源清理:
- 在
connectDB
函数中,虽然没有显式使用defer
关闭数据库连接,但在main
函数中获取数据库连接后,使用defer db.Close()
确保无论后续操作是否成功,数据库连接都会被关闭,避免资源泄露。 - 在
insertData
函数中,使用defer stmt.Close()
确保在函数结束时关闭数据库语句对象,防止资源未释放。
- 在
- 错误处理:
- 每个函数在执行可能出错的操作后,立即检查错误并返回。如果错误发生,后续的函数调用将不会执行,避免了错误传播混乱。
- 在
main
函数中,依次调用各个功能函数,并在每次调用后检查错误。如果出现错误,打印错误信息并返回,从而有效地处理了不同类型的错误。
- 数据库事务处理:
- 在
insertData
函数中,使用数据库事务来确保数据插入的原子性。如果在插入过程中出现任何错误,通过tx.Rollback()
回滚事务,避免部分数据插入成功而部分失败的情况。
- 在
通过这种方式,利用defer
语句结合合理的错误处理流程,实现了高效、优雅的错误处理,同时避免了资源泄露和错误传播混乱。