feat: 完成批量插入的SQL生成功能
This commit is contained in:
174
mapping.go
Normal file
174
mapping.go
Normal file
@@ -0,0 +1,174 @@
|
||||
package td_builder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"manabox.cn/tdengine-mapper/syncmap"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func NewTdMapping() *TdMapping {
|
||||
return &TdMapping{}
|
||||
}
|
||||
|
||||
// TableNamer 定义了一个获取表名的方法。
|
||||
type TableNamer interface {
|
||||
TableName() string
|
||||
}
|
||||
|
||||
type TdMapping struct {
|
||||
modelMates syncmap.Map[string, *StructMate]
|
||||
}
|
||||
|
||||
func (b *TdMapping) scanStruct(data ...any) error {
|
||||
for _, datum := range data {
|
||||
if reflect.TypeOf(datum).Kind() != reflect.Ptr {
|
||||
return fmt.Errorf("需要指针类型:%v", reflect.TypeOf(datum))
|
||||
}
|
||||
|
||||
if mate, err := scan(datum); err != nil {
|
||||
return err
|
||||
} else {
|
||||
b.modelMates.Store(mate.UniqueTypeName, mate)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 提取 struct 内的信息
|
||||
// @param data 包含 struct 类型的数据
|
||||
// @return 返回一个 map[表名][]*TableRowMateria, 如果超级表名为空,则表示普通表
|
||||
func (b *TdMapping) extractStructData(data ...any) (map[string][]*TableRowMateria, error) {
|
||||
result := make(map[string][]*TableRowMateria)
|
||||
|
||||
for _, item := range data {
|
||||
tf, vf := getReflectTypeAndValue(item)
|
||||
uniqueTypeName := getUniqueTypeName(tf)
|
||||
|
||||
// 获取表名
|
||||
var tableName string
|
||||
{
|
||||
if vf.CanAddr() {
|
||||
if a, ok := vf.Addr().Interface().(TableNamer); ok {
|
||||
tableName = a.TableName()
|
||||
}
|
||||
}
|
||||
|
||||
if tableName == "" {
|
||||
if a, ok := item.(TableNamer); ok {
|
||||
tableName = a.TableName()
|
||||
}
|
||||
}
|
||||
|
||||
if tableName == "" {
|
||||
return nil, fmt.Errorf("not import TableName() string func for struct type: %s", uniqueTypeName)
|
||||
}
|
||||
}
|
||||
|
||||
mate, ok := b.modelMates.Load(uniqueTypeName)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("not found struct type: %s", uniqueTypeName)
|
||||
}
|
||||
|
||||
materia := &TableRowMateria{
|
||||
SuperTableName: mate.SuperTableName,
|
||||
TableName: tableName,
|
||||
TagColumns: make([]string, 0, len(mate.TaggedFieldNames)),
|
||||
TagValues: make([]any, 0, len(mate.TaggedFieldNames)),
|
||||
Columns: make([]string, 0, len(mate.DBAnnotatedNames)),
|
||||
Values: make([]any, 0, len(mate.DBAnnotatedNames)),
|
||||
}
|
||||
|
||||
for _, name := range mate.DBAnnotatedNames {
|
||||
// db tag 名称 -- 数据库列名
|
||||
dbColumn := mate.FiledDBNameCache[name]
|
||||
|
||||
field := vf.FieldByIndex(mate.FieldIndexCache[name])
|
||||
for field.Kind() == reflect.Ptr {
|
||||
if field.IsNil() {
|
||||
break
|
||||
}
|
||||
field = field.Elem()
|
||||
}
|
||||
// 字段值
|
||||
dbValue := field.Interface()
|
||||
|
||||
materia.Columns = append(materia.Columns, dbColumn)
|
||||
materia.Values = append(materia.Values, dbValue)
|
||||
}
|
||||
|
||||
for _, name := range mate.TaggedFieldNames {
|
||||
// db tag 名称 -- 数据库列名
|
||||
tagColumn := mate.FiledDBNameCache[name]
|
||||
field := vf.FieldByIndex(mate.FieldIndexCache[name])
|
||||
for field.Kind() == reflect.Ptr {
|
||||
if field.IsNil() {
|
||||
break
|
||||
}
|
||||
field = field.Elem()
|
||||
}
|
||||
// 字段值
|
||||
tagValue := field.Interface()
|
||||
|
||||
materia.TagColumns = append(materia.TagColumns, tagColumn)
|
||||
materia.TagValues = append(materia.TagValues, tagValue)
|
||||
}
|
||||
|
||||
// 添加到结果
|
||||
key := materia.TableName
|
||||
result[key] = append(result[key], materia)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (b *TdMapping) ToInsertSQL(data ...any) (string, error) {
|
||||
// 扫描 struct 类型数据
|
||||
if err := b.scanStruct(data...); err != nil {
|
||||
return "", fmt.Errorf("failed to scan struct: %w", err)
|
||||
}
|
||||
|
||||
// 提取 struct 内的信息
|
||||
tableMap, err := b.extractStructData(data...)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to extract struct data: %w", err)
|
||||
}
|
||||
|
||||
if len(tableMap) == 0 {
|
||||
return "", fmt.Errorf("no data to insert")
|
||||
}
|
||||
|
||||
// 构建 insert 语句
|
||||
/*
|
||||
INSERT INTO
|
||||
表名1 USING 超级表名1 (tag列名) TAGS(tag值) (列名) VALUES (),()
|
||||
表名2 USING 超级表名2 (tag列名)TAGS(tag值) (列名) VALUES (),()
|
||||
*/
|
||||
var buf strings.Builder
|
||||
buf.WriteString("INSERT INTO \n")
|
||||
for _, materials := range tableMap {
|
||||
if len(materials) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
var (
|
||||
rowSql string
|
||||
err error
|
||||
)
|
||||
if materials[0].SuperTableName == "" {
|
||||
// 表名1 (列名) VALUES (),()
|
||||
rowSql, err = buildInsertStatementForNormalTable(materials...)
|
||||
} else {
|
||||
// 表名1 USING 超级表名1 (tag列名) TAGS(tag值) (列名) VALUES (),()
|
||||
rowSql, err = buildInsertStatementForSuperTable(materials...)
|
||||
}
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to build insert statement: %w", err)
|
||||
}
|
||||
|
||||
buf.WriteString(rowSql)
|
||||
buf.WriteString(" \n")
|
||||
}
|
||||
|
||||
return buf.String(), nil
|
||||
}
|
||||
Reference in New Issue
Block a user