package mysql import ( "fmt" "gorm.io/driver/mysql" "gorm.io/gorm" "gorm.io/gorm/schema" "gorm.io/plugin/dbresolver" ) type Config struct { Host string Port int User string Password string Database string Charset string Prefix string ReadHost string ReadPort int ReadUser string ReadPassword string ReadDatabase string } func New(c Config) (*gorm.DB, error) { dsn := buildDSN(c.User, c.Password, c.Host, c.Port, c.Database, c.Charset) db, err := gorm.Open(mysql.New(mysql.Config{ DSN: dsn, DefaultStringSize: 256, DisableDatetimePrecision: true, DontSupportRenameIndex: true, DontSupportRenameColumn: true, SkipInitializeWithVersion: false, }), &gorm.Config{ NamingStrategy: schema.NamingStrategy{ TablePrefix: c.Prefix, }, }) if err != nil { return nil, fmt.Errorf("mysql open: %w", err) } readDSN := buildReadDSN(c) if readDSN != "" { if err := db.Use(dbresolver.Register(dbresolver.Config{ Sources: []gorm.Dialector{mysql.Open(dsn)}, Replicas: []gorm.Dialector{mysql.Open(readDSN)}, Policy: dbresolver.RandomPolicy{}, })); err != nil { return nil, fmt.Errorf("mysql dbresolver: %w", err) } } return db, nil } func buildReadDSN(c Config) string { if c.ReadHost == "" { return "" } port := c.ReadPort if port == 0 { port = c.Port } user := c.ReadUser if user == "" { user = c.User } password := c.ReadPassword if password == "" { password = c.Password } database := c.ReadDatabase if database == "" { database = c.Database } charset := c.Charset if charset == "" { charset = "utf8mb4" } return buildDSN(user, password, c.ReadHost, port, database, charset) } func buildDSN(user, password, host string, port int, database, charset string) string { return fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s&parseTime=True&loc=Local", user, password, host, port, database, charset) }