à propos de sqlx
sqlx est une bibliothèque d'opérations de base de données Golang, qui fournit un ensemble d'API étendues basées database/sql
sur , qui est un sur-ensemble de l'API de bibliothèque native.
Par défaut, sqlx obtiendra les champs de base de données correspondants lors de l'analyse et de l'analyse de la structure, et les priorités sont les suivantes :
- S'il y a une balise db dans la balise, obtenez le nom de champ correspondant de la base de données en fonction de la balise, voir sqlx.go#43 pour le code source .
- S'il n'y a pas de balise db dans la balise, tous les noms de champs en minuscules seront utilisés comme noms de champs correspondants de la base de données. Voir sqlx.go#27 pour le code source .
Selon la spécification de développement SQL et les principes de base de TiDB - Spécification de dénomination d'objet "Il est recommandé d'utiliser des mots anglais significatifs pour la dénomination, séparés par des traits de soulignement au milieu des mots", nous devons changer la deuxième casse "tout en minuscules" en " méthode de nommage souligné" .
Il faut deux étapes pour modifier la fonction de génération de champ de base de données sqlx.
- Écrivez la fonction NameMapper de la nomenclature de soulignement.
- Modifiez la fonction NameMapper par défaut.
Écrivez la méthode de nommage de soulignement NameMapper function
D'après le code source, nous savons que par défaut, sqlx utilise la méthode des chaînes de la bibliothèque strings.ToLower
native pour le traitement des champs.Cette fonction n'a qu'un paramètre nommé nom et type chaîne.
Ensuite, nous pouvons nous référer au code source gorm
de (par défaut, gorm utilise la méthode de nommage souligné pour générer les champs de la base de données) pour extraire toDBName
la fonction, voir gorm - schema/naming.go:117 pour plus de détails .
Voici la fonction de génération de champ de nomenclature de soulignement proposée par l'auteurtoDBName
:
import "strings"
var commonInitialismsReplacer = newCommonInitialismsReplacer()
func newCommonInitialismsReplacer() *strings.Replacer {
// https://github.com/golang/lint/blob/master/lint.go#L770
commonInitialisms := []string{
"API", "ASCII", "CPU", "CSS", "DNS", "EOF", "GUID", "HTML", "HTTP", "HTTPS", "ID", "IP", "JSON", "LHS", "QPS", "RAM", "RHS", "RPC", "SLA", "SMTP", "SSH", "TLS", "TTL", "UID", "UI", "UUID", "URI", "URL", "UTF8", "VM", "XML", "XSRF", "XSS"}
commonInitialismsForReplacer := make([]string, 0, len(commonInitialisms))
for _, initialism := range commonInitialisms {
commonInitialismsForReplacer = append(commonInitialismsForReplacer, initialism, strings.Title(strings.ToLower(initialism)))
}
replacer := strings.NewReplacer(commonInitialismsForReplacer...)
return replacer
}
func toDBName(name string) string {
if name == "" {
return ""
}
var (
value = commonInitialismsReplacer.Replace(name)
buf strings.Builder
lastCase, nextCase, nextNumber bool // upper case == true
curCase = value[0] <= 'Z' && value[0] >= 'A'
)
for i, v := range value[:len(value)-1] {
nextCase = value[i+1] <= 'Z' && value[i+1] >= 'A'
nextNumber = value[i+1] >= '0' && value[i+1] <= '9'
if curCase {
if lastCase && (nextCase || nextNumber) {
buf.WriteRune(v + 32)
} else {
if i > 0 && value[i-1] != '_' && value[i+1] != '_' {
buf.WriteByte('_')
}
buf.WriteRune(v + 32)
}
} else {
buf.WriteRune(v)
}
lastCase = curCase
curCase = nextCase
}
if curCase {
if !lastCase && len(value) > 1 {
buf.WriteByte('_')
}
buf.WriteByte(value[len(value)-1] + 32)
} else {
buf.WriteByte(value[len(value)-1])
}
ret := buf.String()
return ret
}
Modifier la fonction NameMapper par défaut
Selon le code source de sqlx, nous savons NameMapper
qu'il s'agit d' une variable globale de sqlx, nous n'avons donc qu'à modifier la variable pendant le processus d'initialisation du programme.
func init() {
sqlx.NameMapper = toDBName
}
Montrer les résultats
La démo montrera la différence entre l'utilisation de toutes les fonctions de nomenclature en minuscules et le remplacement des traits de soulignement.
Il convient de noter que l'environnement de test utilise golang v1.17, sqlx v1.3.4 et TiDB v1.6.1.
- Nous devons d'abord créer une table de données testTable pour la démonstration.
CREATE TABLE `test_table` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(256) NOT NULL,
`sex` varchar(256) NOT NULL,
`counter` bigint(20) NOT NULL,
`create_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`miss_counter` bigint(20) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `test_table_name_uindex` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin AUTO_INCREMENT=30001
- Ensuite, définissez une structure appelée testTable dans le programme Go, où
MissCounter
les champs seront affectés par NameMapper.
type testTable struct {
ID uint64
Name string // unique index
Sex string
Counter uint64 // 命中计数字段
MissCounter uint64 // 非命中计数字段
CreateAt time.Time
UpdateAt time.Time
}
- Ensuite, nous devons écrire un code d'insertion pour démontrer l'impact des différentes fonctions de génération de champs de base de données.
func main(){
tx, err := db.BeginTxx(ctx, &sql.TxOptions{
})
if err != nil {
log.Error(err)
return
}
const insertSQL = "INSERT INTO test_table (name, sex, counter, miss_counter) VALUES (:name, :sex, :counter, :miss_counter);"
stmt, errPrepare := tx.PrepareNamedContext(ctx, insertSQL)
if errPrepare != nil {
log.Errorf("failed to prepare, err: %v", errPrepare)
_ = tx.Rollback()
return
}
for _, row := range []*testTable{
{
Name: "小李", Sex: "男", Counter: uint64(rand.Int63()), MissCounter: uint64(rand.Int63())},
{
Name: "小白", Sex: "女", Counter: uint64(rand.Int63()), MissCounter: uint64(rand.Int63())},
{
Name: "小黑", Sex: "男", Counter: uint64(rand.Int63()), MissCounter: uint64(rand.Int63())},
{
Name: "小天", Sex: "女", Counter: uint64(rand.Int63()), MissCounter: uint64(rand.Int63())},
} {
res, errExec := stmt.ExecContext(ctx, row)
if errExec != nil {
// for duplicate err
if me, isMySQLErr := errExec.(*mysql.MySQLError); isMySQLErr && me.Number == 1062 /*duplicate is 1062*/ {
log.Infof("skip duplicate err, err: %v, row: %+v", me, row)
continue
}
log.Errorf("failed to stmt exec, err: %v", errExec)
_ = tx.Rollback()
return
}
insertID, _ := res.LastInsertId()
log.Infof("success to stmt exec, row: %+v, id: %d", row, insertID)
}
if errCommit := tx.Commit(); errCommit != nil {
log.Error(errCommit)
return
}
log.Infof("finish")
}
- Dans le cas de l'utilisation de NameMapper en minuscules, l'exécution du programme signalera une erreur
could not find name xxx
.
2021-10-31T16:29:15.029+0800 ERROR transaction/main.go:76 failed to stmt exec, err: could not find name miss_counter in &main.testTable{
ID:0x0, Name:"小李, Sex:"男", Counter:0x4354432fe0f37484, MissCounter:0x45afa115903b7669, CreateAt:time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), UpdateAt:time.Date(1, tim.January, 1, 0, 0, 0, 0, time.UTC)}
- Dans le cas d'un NameMapper qui utilise la notation de soulignement, le programme s'exécute sans problème.
2021-10-31T16:30:27.841+0800 INFO transaction/main.go:81 success to stmt exec, row: &{
ID:0 Name:小李 Sex:男 Counter:6239523519032355576 MissCounter:180608923895024 CreateAt:0001-01-01 00:00:00 +0000 UTC UpdateAt:0001-01-01 00:00:00 +0000 UTC}, id: 23
2021-10-31T16:30:27.842+0800 INFO transaction/main.go:81 success to stmt exec, row: &{
ID:0 Name:小白 Sex:女 Counter:2433768853952726858 MissCounter:7832533413053117 CreateAt:0001-01-01 00:00:00 +0000 UTC UpdateAt:0001-01-01 00:00:00 +0000 UTC}, id: 24
2021-10-31T16:30:27.844+0800 INFO transaction/main.go:81 success to stmt exec, row: &{
ID:0 Name:小黑 Sex:男 Counter:8462818185278118914 MissCounter:1149211500697067 CreateAt:0001-01-01 00:00:00 +0000 UTC UpdateAt:0001-01-01 00:00:00 +0000 UTC}, id: 25
2021-10-31T16:30:27.845+0800 INFO transaction/main.go:81 success to stmt exec, row: &{
ID:0 Name:小天 Sex:女 Counter:23243666993166910 MissCounter:9005766596808865 CreateAt:0001-01-01 00:00:00 +0000 UTC UpdateAt:0001-01-01 00:00:00 +0000 UTC}, id: 26
2021-10-31T16:30:27.848+0800 INFO transaction/main.go:87 finish
Si cet article vous est utile, vous pouvez donner un like à l'auteur, merci.