package db import ( "database/sql" "fmt" "time" "gorm.io/driver/mysql" "gorm.io/driver/postgres" "gorm.io/gorm" "gorm.io/gorm/schema" "zestack.dev/env" ) func Init() error { var dialector gorm.Dialector switch driver := env.String("DB_DRIVER", "postgres"); driver { case "mysql": schemaHelper = &mysqlSchemaHelper{} dialector = openMysql() case "postgres": schemaHelper = &pgsqlSchemaHelper{} dialector = openPostgres() default: return fmt.Errorf("unsupported database driver: %s", driver) } var err error db, err = gorm.Open(dialector, &gorm.Config{ NamingStrategy: schema.NamingStrategy{ TablePrefix: env.String("DB_PREFIX"), SingularTable: env.Bool("DB_SINGULAR_TABLE", false), IdentifierMaxLength: env.Int("DB_IDENTIFIER_MAX_LENGTH", 0), }, Logger: &dbLogger{200 * time.Millisecond}, // 在我们使用模型查询时,使用模型字段而不是通配符来查询。 // https://gorm.io/docs/advanced_query.html#Smart-Select-Fields QueryFields: env.Bool("DB_QUERY_FIELDS", true), // 在 AutoMigrate 或 CreateTable 时,GORM 会自动创建外键约束, // 若要禁用该特性,可将其设置为 true 。 // https://gorm.io/docs/migration.html DisableForeignKeyConstraintWhenMigrating: env.Bool("DB_DISABLE_FOREIGN_KEY_CONSTRAINT", false), // 在 AutoMigrate 或 CreateTable 时,GORM 会自动创建索引, // 若要禁用该特性,可将其设置为 true 。 IgnoreRelationshipsWhenMigrating: env.Bool("DB_IGNORE_RELATIONSHIPS", false), // 开启方言错误转换,GORM 会将不同数据库的特定错误转换为常见的 GORM 错误类型, // 这样,方便我们对错误进行统一处理。 // https://gorm.io/docs/error_handling.html#Dialect-Translated-Errors // https://github.com/go-gorm/gorm/blob/master/errors.go TranslateError: true, }) if err != nil { return err } // 如果使用的是 MySQL 数据库,则启用 InnoDB 引擎。 if db.Dialector.Name() == "mysql" { db.Set("gorm:table_options", "ENGINE=InnoDB") } // 配置连接池 var raw *sql.DB if raw, err = db.DB(); err == nil { err = configRawDB(raw) } return err } func openMysql() gorm.Dialector { return mysql.New(mysql.Config{ DSN: fmt.Sprintf( "%s:%s@tcp(%s:%d)/%s?charset=%s&parseTime=True&loc=%s", env.String("DB_USER", "root"), env.String("DB_AUTH", "password"), env.String("DB_HOST", "localhost"), env.Int("DB_PORT", 3306), env.String("DB_NAME", "test"), env.String("DB_CHARSET", "utf8mb4"), time.Local.String(), ), // string 类型字段的默认长度 DefaultStringSize: uint(env.Int("DB_STRING_SIZE", 256)), // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持 DisableDatetimePrecision: true, // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引 DontSupportRenameIndex: true, // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列 DontSupportRenameColumn: true, }) } func openPostgres() gorm.Dialector { return postgres.Open(fmt.Sprintf( "host=%s user=%s password=%s dbname=%s port=%d sslmode=%s TimeZone=%s", env.String("DB_HOST", "localhost"), env.String("DB_USER", "postgres"), env.String("DB_AUTH", "password"), env.String("DB_NAME", "postgres"), env.Int("DB_PORT", 5432), env.String("DB_SSLMODE", "disable"), time.Local.String(), )) } func configRawDB(db *sql.DB) error { // 用于设置连接池中空闲连接的最大数量。 if maxIdleConns := env.Int("DB_MAX_IDLE_CONNS", 0); maxIdleConns > 0 { db.SetMaxIdleConns(maxIdleConns) } // 设置打开数据库连接的最大数量。 if maxOpenConns := env.Int("DB_MAX_OPEN_CONNS", 0); maxOpenConns > 0 { db.SetMaxOpenConns(maxOpenConns) } // 设置了连接可复用的最大时间。 if connMaxLifetime := env.Duration("DB_CONN_MAX_LIFETIME", 0); connMaxLifetime > 0 { db.SetConnMaxLifetime(connMaxLifetime) } return db.Ping() }