github.com/goravel/framework@v1.13.9/database/gorm/gorm.go (about)

     1  package gorm
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"log"
     7  	"os"
     8  	"time"
     9  
    10  	"github.com/google/wire"
    11  	gormio "gorm.io/gorm"
    12  	gormlogger "gorm.io/gorm/logger"
    13  	"gorm.io/gorm/schema"
    14  	"gorm.io/plugin/dbresolver"
    15  
    16  	"github.com/goravel/framework/contracts/config"
    17  	databasecontract "github.com/goravel/framework/contracts/database"
    18  	"github.com/goravel/framework/database/db"
    19  	"github.com/goravel/framework/support/carbon"
    20  )
    21  
    22  var GormSet = wire.NewSet(NewGormImpl, wire.Bind(new(Gorm), new(*GormImpl)))
    23  var _ Gorm = &GormImpl{}
    24  
    25  type Gorm interface {
    26  	Make() (*gormio.DB, error)
    27  }
    28  
    29  type GormImpl struct {
    30  	config     config.Config
    31  	connection string
    32  	dbConfig   db.Config
    33  	dialector  Dialector
    34  	instance   *gormio.DB
    35  }
    36  
    37  func NewGormImpl(config config.Config, connection string, dbConfig db.Config, dialector Dialector) *GormImpl {
    38  	return &GormImpl{
    39  		config:     config,
    40  		connection: connection,
    41  		dbConfig:   dbConfig,
    42  		dialector:  dialector,
    43  	}
    44  }
    45  
    46  func (r *GormImpl) Make() (*gormio.DB, error) {
    47  	readConfigs := r.dbConfig.Reads()
    48  	writeConfigs := r.dbConfig.Writes()
    49  	if len(writeConfigs) == 0 {
    50  		return nil, errors.New("not found database configuration")
    51  	}
    52  
    53  	writeDialectors, err := r.dialector.Make([]databasecontract.Config{writeConfigs[0]})
    54  	if err != nil {
    55  		return nil, fmt.Errorf("init gorm dialector error: %v", err)
    56  	}
    57  
    58  	if err := r.init(writeDialectors[0]); err != nil {
    59  		return nil, err
    60  	}
    61  
    62  	if err := r.configurePool(); err != nil {
    63  		return nil, err
    64  	}
    65  
    66  	if err := r.configureReadWriteSeparate(readConfigs, writeConfigs); err != nil {
    67  		return nil, err
    68  	}
    69  
    70  	return r.instance, nil
    71  }
    72  
    73  func (r *GormImpl) configurePool() error {
    74  	sqlDB, err := r.instance.DB()
    75  	if err != nil {
    76  		return err
    77  	}
    78  
    79  	sqlDB.SetMaxIdleConns(r.config.GetInt("database.pool.max_idle_conns", 10))
    80  	sqlDB.SetMaxOpenConns(r.config.GetInt("database.pool.max_open_conns", 100))
    81  	sqlDB.SetConnMaxIdleTime(time.Duration(r.config.GetInt("database.pool.conn_max_idletime", 3600)) * time.Second)
    82  	sqlDB.SetConnMaxLifetime(time.Duration(r.config.GetInt("database.pool.conn_max_lifetime", 3600)) * time.Second)
    83  
    84  	return nil
    85  }
    86  
    87  func (r *GormImpl) configureReadWriteSeparate(readConfigs, writeConfigs []databasecontract.Config) error {
    88  	if len(readConfigs) == 0 || len(writeConfigs) == 0 {
    89  		return nil
    90  	}
    91  
    92  	readDialectors, err := r.dialector.Make(readConfigs)
    93  	if err != nil {
    94  		return err
    95  	}
    96  
    97  	writeDialectors, err := r.dialector.Make(writeConfigs)
    98  	if err != nil {
    99  		return err
   100  	}
   101  
   102  	return r.instance.Use(dbresolver.Register(dbresolver.Config{
   103  		Sources:           writeDialectors,
   104  		Replicas:          readDialectors,
   105  		Policy:            dbresolver.RandomPolicy{},
   106  		TraceResolverMode: true,
   107  	}))
   108  }
   109  
   110  func (r *GormImpl) init(dialector gormio.Dialector) error {
   111  	var logLevel gormlogger.LogLevel
   112  	if r.config.GetBool("app.debug") {
   113  		logLevel = gormlogger.Info
   114  	} else {
   115  		logLevel = gormlogger.Error
   116  	}
   117  
   118  	logger := NewLogger(log.New(os.Stdout, "\r\n", log.LstdFlags), gormlogger.Config{
   119  		SlowThreshold:             200 * time.Millisecond,
   120  		LogLevel:                  gormlogger.Info,
   121  		IgnoreRecordNotFoundError: true,
   122  		Colorful:                  true,
   123  	})
   124  	instance, err := gormio.Open(dialector, &gormio.Config{
   125  		DisableForeignKeyConstraintWhenMigrating: true,
   126  		SkipDefaultTransaction:                   true,
   127  		Logger:                                   logger.LogMode(logLevel),
   128  		NowFunc: func() time.Time {
   129  			return carbon.Now().ToStdTime()
   130  		},
   131  		NamingStrategy: schema.NamingStrategy{
   132  			TablePrefix:   r.config.GetString(fmt.Sprintf("database.connections.%s.prefix", r.connection)),
   133  			SingularTable: r.config.GetBool(fmt.Sprintf("database.connections.%s.singular", r.connection)),
   134  		},
   135  	})
   136  	if err != nil {
   137  		return err
   138  	}
   139  
   140  	r.instance = instance
   141  
   142  	return nil
   143  }