常见问题汇总

为什么强制使用Context

Context是zorm实现事务传播的核心, Context也是go推荐的参数传递方式, 使用Context是个好习惯 :)

达梦数据库问题

  • 如果部署的机器上没有dm库会出现dmconfig找不到的异常。
    达梦的text类型会映射为dm.DmClob,string不能接收,需要实现zorm.CustomDriverValueConver接口,自定义扩展处理
import (
	// 00.引入数据库驱动
	"gitee.com/chunanyong/dm"
	"io"
)

// CustomDMText 实现ICustomDriverValueConver接口,扩展自定义类型,例如 达梦数据库TEXT类型,映射出来的是dm.DmClob类型,无法使用string类型直接接收
type CustomDMText struct{}

// GetDriverValue 根据数据库列类型,返回driver.Value的实例,struct属性类型
// map接收或者字段不存在,无法获取到structFieldType,会传入nil
func (dmtext CustomDMText) GetDriverValue(ctx context.Context, columnType *sql.ColumnType, structFieldType *reflect.Type) (driver.Value, error) {
	// 如果需要使用structFieldType,需要先判断是否为nil
	// if structFieldType != nil {
	// }

	return &dm.DmClob{}, nil
}

// ConverDriverValue 数据库列类型,GetDriverValue返回的driver.Value的临时接收值,struct属性类型
// map接收或者字段不存在,无法获取到structFieldType,会传入nil
// 返回符合接收类型值的指针,指针,指针!!!!
func (dmtext CustomDMText) ConverDriverValue(ctx context.Context, columnType *sql.ColumnType, tempDriverValue driver.Value, structFieldType *reflect.Type) (interface{}, error) {
	// 如果需要使用structFieldType,需要先判断是否为nil
	// if structFieldType != nil {
	// }

	// 类型转换
	dmClob, isok := tempDriverValue.(*dm.DmClob)
	if !isok {
		return tempDriverValue, errors.New("->ConverDriverValue-->转换至*dm.DmClob类型失败")
	}
	if dmClob == nil || !dmClob.Valid {
		return new(string), nil
	}
	// 获取长度
	dmlen, errLength := dmClob.GetLength()
	if errLength != nil {
		return dmClob, errLength
	}

	// int64转成int类型
	strInt64 := strconv.FormatInt(dmlen, 10)
	dmlenInt, errAtoi := strconv.Atoi(strInt64)
	if errAtoi != nil {
		return dmClob, errAtoi
	}

	// 读取字符串
	str, errReadString := dmClob.ReadString(1, dmlenInt)

	// 处理空字符串或NULL造成的EOF错误
	if errReadString == io.EOF {
		return new(string), nil
	}

	return &str, errReadString
}
// RegisterCustomDriverValueConver 注册自定义的字段处理逻辑,用于驱动无法直接转换的场景,例如达梦的 TEXT 无法直接转化成 string
// 一般是放到init方法里进行注册
func init() {
	// dialectColumnType 值是 Dialect.字段类型 ,例如 dm.TEXT
    zorm.RegisterCustomDriverValueConver("dm.TEXT", CustomDMText{})
}
  • 出现报错Query-->sqlRowsValues异常:sqlRowsValues-->conver.ConverDriverValue异常:EOF 已经调整了CustomDMText的代码,完整的复制过去,配合zorm 1.6.4版本即可.

  • Linux下启动报错invalid memory address or nil pointer dereferenceinvalidmemory .png
    由于Linux(Ubuntu)下缺少dm_svc.conf文件,手动创建该文件后就可以了

if filePath == "" {
    switch runtime.GOOS {
        case "windows":
	    filePath = os.Getenv("SystemRoot") + "\\system32\\dm_svc.conf"
        case "linux":
	    filePath = "/etc/dm_svc.conf"
        default:
	    return
    }
}

查看Windows系统下的dm_svc.conf文件,其内容如下:

TIME_ZONE=(480)
LANGUAGE=(cn)

达梦使用time作为where条件,需要注意时区问题,建议使用字符串代替time, https://eco.dameng.com/community/question/936924eb9861e6d429114e87d6f3a854 最新的达梦驱动 gitee.com/chunanyong/dm v1.8.9 已修复

如何使用like

finder.Append("WHERE name like ? ", "小明%")

如何使用join

finder := zorm.NewFinder().Append("SELECT t1.*,t2.* FROM  user t1 left join class t2 where t1.id = t2.userid" ) 

如何取消sql注入验证

解决sql中使用单引号报错(不推荐)

finder.InjectionCheck = false

如何获取自增ID值

使用zorm.Insert保存单个对象,保存成功之后,自增主键会赋值到对象的主键属性.
使用zorm.InsertEntityMap保存单个EntityMap,不要entityMap.Set主键的值,保存成功之后,自增主键会Set到entityMap中.
批量保存时,不支持返回每条数据的自增主键值.

如何配置多个数据库

使用BindContextDBConnection绑定不同ctx,在使用不同库时传入不同ctx.

package database

import (
	"context"
	"fmt"
	"gitee.com/chunanyong/zorm"
	_ "github.com/taosdata/driver-go/v3/taosRestful"
	"lauv/oamm/config"
	"lauv/oamm/logger"
	"lauv/oamm/util"
	"strings"
)

var tdCtx = context.Background()
var tdbDao *zorm.DBDao

func InitTdengine() {
	tdConfig := config.NacosConfig.Tdengine
	if tdConfig.Host == "" {
		logger.Log.Info("td start failed ,because no config")
		return
	}
	//dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/", config.GLOBAL.PushTD.Username, config.GLOBAL.PushTD.Password, config.GLOBAL.PushTD.Host, config.GLOBAL.PushTD.Port)
	dbDaoConfig := zorm.DataSourceConfig{
		//DSN 数据库的连接字符串
		//DSN: dsn,
		DSN: fmt.Sprintf("%s:%s@http(%s:%d)/", tdConfig.User, tdConfig.Password, tdConfig.Host, tdConfig.Port),
		//数据库驱动名称:mysql,postgres,oci8,sqlserver,sqlite3,clickhouse,dm,kingbase,aci 和DBType对应,处理数据库有多个驱动
		DriverName: "taosRestful",
		//数据库类型(方言判断依据):mysql,postgresql,oracle,mssql,sqlite,clickhouse,dm,kingbase,shentong 和 DriverName 对应,处理数据库有多个驱动
		Dialect: "tdengine",
		//MaxOpenConns 数据库最大连接数 默认50
		MaxOpenConns: 10,
		//MaxIdleConns 数据库最大空闲连接数 默认50
		MaxIdleConns: 10,
		//ConnMaxLifetimeSecond 连接存活秒时间. 默认600(10分钟)后连接被销毁重建.避免数据库主动断开连接,造成死连接.MySQL默认wait_timeout 28800秒(8小时)
		ConnMaxLifetimeSecond: 600,
		DisableTransaction:    true, // 禁用全局事务 td没有事务,
		SlowSQLMillis:         0,
	}
	zorm.FuncPrintSQL = PrintSQL
	zorm.FuncLogError = LogError
	zorm.FuncLogPanic = LogPanic
	var err error
	tdbDao, err = zorm.NewDBDao(&dbDaoConfig)
	util.CheckErr(err, "failed to establish library connection")
	tdCtx, err = tdbDao.BindContextDBConnection(tdCtx)
	util.CheckErr(err, "Failed to bind DBDao")
}

func RunTdengineSql(sqlScript string, databaseName string) error {

	//先切换库
	sqlList := strings.Split(sqlScript, ";")
	finder := zorm.NewFinder()
	finder.Append(fmt.Sprintf("use %s", databaseName))
	_, err := zorm.UpdateFinder(tdCtx, finder)
	if err != nil {
		return err
	}

	for i := 0; i < len(sqlList)-1; i++ {
		finder = zorm.NewFinder()
		//关闭sql注入检查
		finder.InjectionCheck = false
		finder.Append(sqlList[i])
		_, err = zorm.UpdateFinder(tdCtx, finder)
		if err != nil {
			return err
		}
	}
	return nil
}