oracle

package module
v1.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 31, 2026 License: MIT Imports: 28 Imported by: 0

README

GORM Oracle Driver

基于 go-ora 实现的 GORM Oracle 数据库驱动,内置版本感知能力、驱动抽象层(当前仅支持 go-ora)、自定义回调体系(INSERT/UPDATE/DELETE/QUERY)与完整测试套件。

特性

  • 版本感知:自动识别 Oracle 数据库版本,按版本适配 SQL 语法与类型
    • 12c+:原生 IDENTITY 列、OFFSET / FETCH 分页、DEFAULT <seq>.NEXTVAL
    • 11g:自动创建序列 + BEFORE INSERT 触发器模拟自增,分页改写为 ROWNUM
    • 21c+:原生 BOOLEAN 列类型(更早版本用 NUMBER(1) 模拟)
    • 23ai:支持 VECTOR 类型(AI Vector Search)
    • 12c+(Extended):VARCHAR2 支持最大 32k 字节,超出自动降级为 CLOB
  • 驱动抽象层driver_adapter):统一 go-ora(纯 Go)驱动的能力差异;godror 支持为路线图项,尚未接线启用
  • 自定义回调体系
    • INSERT ... RETURNING INTO:支持默认值/自增字段回填,批量插入逐行执行保证一致性与返回值正确性
    • ON CONFLICTMERGE INTO:自动改写为 Oracle MERGE 语法
    • 单行 UPDATE/DELETE ... RETURNING INTO
    • WHERE 安全检查:无有效 WHERE 条件时拒绝执行 UPDATE/DELETE,避免全表误操作(软删除条件除外)
    • 软删除支持
  • 数据迁移(Migrator):
    • 表/列/索引/约束的增删改查,列名大小写自动映射(Oracle 返回大写列名)
    • 从数据字典(USER_TAB_COLUMNS)获取真实数据类型,避免 AutoMigrate 误判触发多余 ALTER
    • Oracle 不支持原生 ON UPDATE 外键操作,自动生成触发器模拟 CASCADE / SET NULL
    • Oracle 保留字自动加引号
  • 命名策略统一转换为大写

环境要求

  • Oracle 11g、12c 及以上(支持 21c、23ai 等新版本)
  • Golang 1.22+
  • GORM v1.31.2+
  • 底层驱动默认使用 go-ora v2(纯 Go 实现,无需安装 ODPI-C

安装

go get github.com/charlienet/oracle

快速开始

import (
    "gorm.io/gorm"

    oracle "github.com/charlienet/oracle"
)

func main() {
    dsn := "oracle://user:password@127.0.0.1:1521/XE?SSL=false"
    db, err := gorm.Open(oracle.Open(dsn), &gorm.Config{})
    if err != nil {
        panic(err)
    }
    // do something...
}
DSN 格式

DSN 使用 go-ora 的 URL 格式:

oracle://user:password@host:port/service?SSL=false&CONNECTION TIMEOUT=90&SOCKET TIMEOUT=90

常用参数:

  • SSL:是否启用 TLS 加密
  • CONNECTION TIMEOUT:连接建立超时(秒),go-ora v2.9.0 起该参数只控制连接建立
  • SOCKET TIMEOUT:socket 读写超时(秒),需要读超时保护时配合 CONNECTION TIMEOUT 一起设置
  • LANGUAGE / TERRITORY:会话语言与地区,如 LANGUAGE=SIMPLIFIED+CHINESE&TERRITORY=CHINA

配置项

使用 oracle.New(Config{}) 可获得更多配置能力:

import (
    "gorm.io/gorm"

    oracle "github.com/charlienet/oracle"
    "github.com/charlienet/oracle/driver_adapter"
)

db, err := gorm.Open(oracle.New(oracle.Config{
    DSN:                  "oracle://user:password@127.0.0.1:1521/XE",
    DriverType:           driver_adapter.DriverGoOra, // 驱动类型:go-ora(默认,当前唯一受支持)
    SkipQuoteIdentifiers: false,                      // 是否跳过标识符引用
    DBName:               "SCOTT",                    // 指定 Schema(表名将带 Schema 前缀)
    // Conn: 传入已存在的 *sql.DB 连接池
}), &gorm.Config{})
配置项 说明
DSN 连接串
DriverType 底层驱动类型:driver_adapter.DriverGoOra(默认;当前唯一受支持。DriverGodror 为预留值,未接线,使用会失败)
SkipQuoteIdentifiers true 时不引用标识符
DBName 指定 Schema 名,开启后表名自动带上 SCHEMA.TABLE 前缀
Conn 直接传入已建立的连接池(*sql.DB),此时忽略 DSN
DefaultStringSize 字符串字段未指定大小时的默认长度(默认 1024)
驱动抽象层

driver_adapter 包统一了 Oracle 驱动的能力差异(输出参数、LOB、批量数据、多行 RETURNING 等能力探测)。

当前仅支持 go-ora(默认驱动名 "oracle")。driver_adapter 中保留的 godror 实现(driver_adapter/godror.go,受 go:build godror 约束)为路线图项,尚未在 Initialize 中接线启用;将 Config.DriverType 设置为 DriverGodror 不会生效,使用 godror 会失败。

版本适配行为

特性 Oracle 11g Oracle 12c+ Oracle 21c+ Oracle 23ai
自增主键 序列 + BEFORE INSERT 触发器 GENERATED BY DEFAULT AS IDENTITY 同 12c 同 12c
DEFAULT <seq>.NEXTVAL 建表后创建触发器实现 原生 DEFAULT 子句 同 12c 同 12c
分页(Limit/Offset) ROWNUM 改写 OFFSET n ROWS FETCH NEXT n ROWS ONLY 同 12c 同 12c
BOOLEAN NUMBER(1) NUMBER(1) 原生 BOOLEAN 原生 BOOLEAN
超长字符串(>4000) CLOB VARCHAR2(n)(32k) 同 12c 同 12c
VECTOR 类型 不支持 不支持 不支持 支持

版本通过连接后执行 select version from product_component_version where rownum = 1 自动探测,无需手动配置。

常用操作示例

// 创建(自动回填自增主键/默认值字段)
user := User{Name: "Alice", Email: "alice@example.com"}
db.Create(&user) // user.ID 自动回填

// 批量插入
users := []User{{Name: "A"}, {Name: "B"}}
db.Create(&users)

// Upsert(自动改写为 MERGE INTO)
db.Clauses(clause.OnConflict{DoUpdates: clause.AssignmentColumns([]string{"name"})}).
    Create(&user)

// 更新(无 WHERE 条件会被拒绝)
db.Model(&User{}).Where("id = ?", 1).Update("name", "Bob")

// 删除(无 WHERE 条件会被拒绝;带 DeletedAt 字段时自动软删除)
db.Delete(&User{}, 1)
db.Unscoped().Delete(&User{}, 1) // 强制物理删除

注意事项与已知限制

  • UPDATE / DELETE 执行了 WHERE 安全检查:缺少有效条件(包括仅有软删除条件)时返回 missing WHERE condition 错误
  • Oracle 仅支持单行 RETURNING:多行 UPDATE 不启用 RETURNING 回填;go-ora 不支持批量 INSERT + RETURNING,驱动采用逐行插入保证返回值正确
  • 创建表时若关联关系声明了 ON UPDATE CASCADE / SET NULL,驱动会自动生成同名触发器;删除表时需先删除依赖的表或使用 CASCADE CONSTRAINTS(已内置)
  • 11g 下通过序列 + 触发器模拟自增时,触发器和序列按 SEQ_<table> / TRG_<table> 命名,删除表会级联清理
  • 布尔值在写入时转换为 1/0,读取时转换回 Go bool
  • 事务隔离级别仅支持默认值(即 READ COMMITTED):显式指定隔离级别(如 SERIALIZABLE,甚至显式 READ COMMITTED)受 go-ora 驱动限制会报错,详见 LIMITATIONS.md

测试

tests/ 目录下为集成测试套件(创建、查询、更新、删除、软删除、Hook、迁移、序列、MERGE 等),需要真实的 Oracle 数据库:

ORACLE_DSN="oracle://user:password@host:1521/service" go test ./tests/...

驱动单元测试(无需数据库):

go test ./...

License

LICENSE

Documentation

Index

Constants

View Source
const (
	OracleVersion10 = 10 // Oracle 10g
	OracleVersion11 = 11 // Oracle 11g(不含 IDENTITY 列、OFFSET/FETCH 分页)
	OracleVersion12 = 12 // Oracle 12c(引入 IDENTITY 列、OFFSET/FETCH 分页;12.1 起支持 Extended 32k VARCHAR2)
	OracleVersion18 = 18 // Oracle 18c(12.2 的再版)
	OracleVersion19 = 19 // Oracle 19c
	OracleVersion21 = 21 // Oracle 21c(引入原生 BOOLEAN 列类型)
	OracleVersion23 = 23 // Oracle 23ai(引入 VECTOR 类型)
)

Oracle 版本主版本号常量(对应各版本引入的数据库特性)

View Source
const RowNumberAliasForOracle11 = "ROW_NUM"

Variables

View Source
var ReservedWordsList = []string{}/* 201 elements not displayed */

Functions

func ConvertNameToFormat

func ConvertNameToFormat(x string) string

func Create

func Create(db *gorm.DB)

func Delete

func Delete(db *gorm.DB)

func GetOracleDriver added in v1.1.0

func GetOracleDriver(db *gorm.DB) (*go_ora.OracleDriver, error)

GetOracleDriver 返回底层 go-ora 驱动 P2-5: 解决 db.Driver() 返回 *enumNormalizeDriver 导致用户类型断言失败的问题

func InParam added in v1.1.0

func InParam(value interface{}) interface{}

InParam 创建一个 IN 参数(通常不需要,因为普通参数就是 IN 参数) 此函数主要用于明确语义

func IsReservedWord

func IsReservedWord(v string) bool

func New

func New(config Config) gorm.Dialector

func Open

func Open(dsn string) gorm.Dialector

func Query

func Query(db *gorm.DB)

Query 是 Oracle 特定的查询回调函数 处理查询前后的数据转换和列名映射

func ToDriverParam added in v1.1.0

func ToDriverParam(p Param) go_ora.Out

ToDriverParam 将本地 Param 转换为底层驱动的 Out 结构 此函数在驱动层使用,业务代码不应直接调用

func Update

func Update(db *gorm.DB)

Types

type Config

type Config struct {
	DriverName        string
	DSN               string
	Conn              gorm.ConnPool //*sql.DB
	DefaultStringSize uint
	DBName            string
	DBVer             string
	MaxStringSize     MaxStringSize // MAX_STRING_SIZE 探测结果(三态:Unknown/Standard/Extended),由 Initialize 探测数据库参数后填充;未探测/探测失败(含 11g)时为零值 Unknown,按 STANDARD 保守处理
	// DriverType 所请求的驱动类型。当前仅支持 go-ora:生产链路固定使用
	// go-ora 驱动,设置任何值(含 DriverGodror)均不改变实际驱动选择;
	// godror 支持未接线(Initialize 的驱动路径不读取该字段,仅 GetAdapter
	// 在零值 "" 时回退为 DriverGoOra)。保持零值即可;显式设置为非 "go-ora"
	// 的值时,Initialize 会打印一条告警提示配置无效。
	DriverType           driver_adapter.DriverType
	SkipQuoteIdentifiers bool // 新增:是否跳过标识符引用
}

type Dialector

type Dialector struct {
	*Config
}

func (Dialector) BindVarTo

func (d Dialector) BindVarTo(writer clause.Writer, stmt *gorm.Statement, v any)

func (Dialector) ClauseBuilders

func (d Dialector) ClauseBuilders() map[string]clause.ClauseBuilder

ClauseBuilders 返回 Oracle 方言的 clause 构建器集合。 版本门控统一委托 supportsFetchOffset(单一来源,避免内联重复判定):

  • 版本解析失败(空/不可解析,oracleMajor==0)时保守使用 11g 的 ROWNUM 方案, 避免在 11g 下使用 12c+ 的 FETCH NEXT 语法导致 ORA-00933;
  • >= 12c 使用 OFFSET/FETCH 分页语法。

同时注册 "FOR" 子句构建器(gorm 的 clause.Locking.Name() 返回 "FOR", 对应 queryClauses 中的 "FOR"):输出 Oracle 行锁语法 FOR UPDATE / FOR SHARE 退化 / NOWAIT / SKIP LOCKED,其中 SKIP LOCKED 按 12c+ 版本门控。

func (Dialector) DataTypeOf

func (d Dialector) DataTypeOf(field *schema.Field) string

func (Dialector) DefaultValueOf

func (d Dialector) DefaultValueOf(*schema.Field) clause.Expression

func (Dialector) DummyTableName

func (d Dialector) DummyTableName() string

func (Dialector) Explain

func (d Dialector) Explain(sql string, vars ...any) string

func (Dialector) GetAdapter

func (d Dialector) GetAdapter() driver_adapter.Adapter

GetAdapter 返回驱动适配器。 注意:Config.DriverType 为预留字段,当前未接线——生产链路固定使用 go-ora 驱动(Initialize 的驱动路径不读取该字段);godror 适配器 (driver_adapter/godror.go,受 build tag godror 约束)为预留实现, 未接入 Initialize。此方法仅保证默认返回 go-ora 适配器,供既有调用兼容。

func (*Dialector) GetDBConn added in v1.1.0

func (d *Dialector) GetDBConn() (*sql.DB, error)

GetDBConn 返回底层数据库连接 P1-7: 实现此方法以支持通过 GORM API 获取底层 *sql.DB

func (Dialector) Initialize

func (d Dialector) Initialize(db *gorm.DB) (err error)

func (Dialector) Migrator

func (d Dialector) Migrator(db *gorm.DB) gorm.Migrator

func (Dialector) Name

func (d Dialector) Name() string

func (Dialector) QuoteTo

func (d Dialector) QuoteTo(writer clause.Writer, str string)

func (Dialector) RewriteLimit

func (d Dialector) RewriteLimit(c clause.Clause, builder clause.Builder)

func (Dialector) RewriteLimit11

func (d Dialector) RewriteLimit11(c clause.Clause, builder clause.Builder)

Oracle11 Limit

func (Dialector) RollbackTo

func (d Dialector) RollbackTo(tx *gorm.DB, name string) error

func (Dialector) SavePoint

func (d Dialector) SavePoint(tx *gorm.DB, name string) error

func (Dialector) Translate added in v1.1.0

func (d Dialector) Translate(err error) error

Translate 将 Oracle 错误映射为 GORM 标准错误。 优先基于 go-ora 结构化错误码(network.OracleError.ErrCode)映射; 无法结构化提取时回退到错误文本匹配(兼容纯文本/自定义包装错误)。 其余无对应 gorm.Err* 的 ORA 错误(如 ORA-00060 死锁、ORA-01722 类型转换 失败、ORA-12899 值过大、ORA-01438 值超出精度等)保持原样返回,不做包裹。

type MaxStringSize added in v1.1.0

type MaxStringSize int

MaxStringSize 表示数据库 MAX_STRING_SIZE 参数的三态探测结果

const (
	MaxStringSizeUnknown  MaxStringSize = iota // 未探测/探测失败(含 11g),保守按 STANDARD 处理
	MaxStringSizeStandard                      // VARCHAR2 上限 4000 字节
	MaxStringSizeExtended                      // VARCHAR2 上限 32767 字节
)

type Migrator

type Migrator struct {
	migrator.Migrator
}

func (Migrator) AddColumn

func (m Migrator) AddColumn(value any, field string) error

func (Migrator) AlterColumn

func (m Migrator) AlterColumn(value any, field string) error

func (Migrator) AlterDataTypeOf

func (m Migrator) AlterDataTypeOf(stmt *gorm.Statement, field *schema.Field) (expr clause.Expr)

func (Migrator) ColumnTypes

func (m Migrator) ColumnTypes(value any) ([]gorm.ColumnType, error)

ColumnTypes return columnTypes []gorm.ColumnType and execErr error

func (Migrator) CreateConstraint

func (m Migrator) CreateConstraint(value any, name string) error

func (Migrator) CreateOnUpdateTrigger

func (m Migrator) CreateOnUpdateTrigger(value any, rel *schema.Relationship) error

CreateOnUpdateTrigger 创建 ON UPDATE 触发器 Oracle 不支持原生的 ON UPDATE 外键操作,需要通过触发器模拟

func (Migrator) CreateTable

func (m Migrator) CreateTable(values ...any) error

func (Migrator) CurrentDatabase

func (m Migrator) CurrentDatabase() (name string)

func (Migrator) DropColumn

func (m Migrator) DropColumn(value any, name string) error

func (Migrator) DropConstraint

func (m Migrator) DropConstraint(value any, name string) error

func (Migrator) DropIndex

func (m Migrator) DropIndex(value any, name string) error

func (Migrator) DropOnUpdateTrigger

func (m Migrator) DropOnUpdateTrigger(value any, rel *schema.Relationship) error

DropOnUpdateTrigger 删除 ON UPDATE 触发器

func (Migrator) DropTable

func (m Migrator) DropTable(values ...any) error

func (Migrator) DropView added in v1.1.0

func (m Migrator) DropView(name string) error

DropView 删除视图(Oracle 不支持 DROP VIEW IF EXISTS,使用 PL/SQL 异常处理保证幂等) 覆写 GORM 默认实现(使用 DROP VIEW IF EXISTS,Oracle 不支持)

func (Migrator) FullDataTypeOf

func (m Migrator) FullDataTypeOf(field *schema.Field) (expr clause.Expr)

FullDataTypeOf 返回字段的完整数据库类型(版本感知的默认值处理)。 GORM 标准实现会把 DefaultValue 直接拼成 "DEFAULT xxx",对 11g 下引用序列的 NEXTVAL 默认值会生成非法 SQL(ORA-00984),因此在此重写: 11g 下 NEXTVAL 默认值不生成 DEFAULT 子句,改由 CreateTable 流程在建表后 创建 BEFORE INSERT 触发器实现等价语义(见 createSequenceDefaultTrigger)。

func (Migrator) GetIndexes added in v1.1.0

func (m Migrator) GetIndexes(value interface{}) ([]gorm.Index, error)

GetIndexes 返回表的所有索引(Oracle 数据字典 USER_INDEXES)

func (Migrator) GetTables added in v1.1.0

func (m Migrator) GetTables() ([]string, error)

GetTables 返回当前用户的所有表名(Oracle 数据字典 USER_TABLES) 覆写 GORM 默认实现(使用 information_schema.tables,Oracle 不支持)

func (Migrator) GetTypeAliases added in v1.0.13

func (m Migrator) GetTypeAliases(databaseTypeName string) []string

GetTypeAliases 返回字段逻辑类型在 Oracle 数据字典中的别名,用于 MigrateColumn 的类型比对:Oracle 把 INTEGER/SMALLINT 等数值列统一报告为 NUMBER, 覆写默认实现避免 AutoMigrate 反复误判为类型差异而重复 ALTER。

MigrateColumn 匹配逻辑(gorm@v1.31.2/migrator/migrator.go:484-493):

if !strings.HasPrefix(fullDataType, realDataType) {
    aliases := m.DB.Migrator().GetTypeAliases(realDataType)
    for _, alias := range aliases {
        if strings.HasPrefix(fullDataType, alias) { isSameType = true }
    }
}

因此 aliases 应是 DataTypeOf 输出值的前缀(如 "integer"、"smallint"), 而非 Oracle 侧的类型名 "number"。

func (Migrator) HasColumn

func (m Migrator) HasColumn(value any, field string) bool

func (Migrator) HasConstraint

func (m Migrator) HasConstraint(value any, name string) bool

func (Migrator) HasIndex

func (m Migrator) HasIndex(value any, name string) bool

func (Migrator) HasTable

func (m Migrator) HasTable(value any) bool

func (Migrator) MigrateColumn added in v1.1.0

func (m Migrator) MigrateColumn(value interface{}, field *schema.Field, columnType gorm.ColumnType) error

MigrateColumn 迁移列定义(Oracle 使用 MODIFY 语法,而非 ALTER COLUMN) 覆写 GORM 默认实现,适配 Oracle 的类型系统和语法: 1. Oracle 使用 NUMBER 统一表示数值类型,需通过 GetTypeAliases 处理类型别名 2. Oracle 使用 ALTER TABLE ... MODIFY ... 而非 ALTER COLUMN ... TYPE

func (Migrator) MigrateColumnUnique added in v1.1.0

func (m Migrator) MigrateColumnUnique(value interface{}, field *schema.Field, columnType gorm.ColumnType) error

MigrateColumnUnique 处理唯一约束变更

func (Migrator) RenameColumn added in v1.1.0

func (m Migrator) RenameColumn(value interface{}, oldName, newName string) error

RenameColumn 重命名列(12c+ 支持,11g 不支持)

func (Migrator) RenameIndex

func (m Migrator) RenameIndex(value any, oldName, newName string) error

https://docs.oracle.com/database/121/SPATL/alter-index-rename.htm

func (Migrator) RenameTable

func (m Migrator) RenameTable(oldName, newName any) (err error)

func (Migrator) TableType added in v1.1.0

func (m Migrator) TableType(value interface{}) (gorm.TableType, error)

TableType 返回表类型(TABLE/VIEW 等)

func (Migrator) TryRemoveOnUpdate

func (m Migrator) TryRemoveOnUpdate(values ...any) error

type Namer

type Namer struct {
	NamingStrategy schema.Namer
	DBName         string
}

func (Namer) CheckerName

func (n Namer) CheckerName(table, column string) (name string)

func (Namer) ColumnName

func (n Namer) ColumnName(table, column string) (name string)

func (Namer) IndexName

func (n Namer) IndexName(table, column string) (name string)

func (Namer) JoinTableName

func (n Namer) JoinTableName(table string) (name string)

func (Namer) RelationshipFKName

func (n Namer) RelationshipFKName(relationship schema.Relationship) (name string)

func (Namer) SchemaName

func (n Namer) SchemaName(table string) string

func (Namer) TableName

func (n Namer) TableName(table string) (name string)

func (Namer) UniqueName

func (n Namer) UniqueName(table, column string) string

type OracleGobSerializer added in v1.1.0

type OracleGobSerializer struct{}

OracleGobSerializer 是 Oracle 优化的 GobSerializer 支持从 string 类型(VARCHAR2 列)解码 GOB 数据

func (OracleGobSerializer) Scan added in v1.1.0

func (OracleGobSerializer) Scan(ctx context.Context, field *schema.Field, dst reflect.Value, dbValue interface{}) (err error)

Scan 实现 serializer interface 支持从 []byte 和 string 类型解码

func (OracleGobSerializer) Value added in v1.1.0

func (OracleGobSerializer) Value(ctx context.Context, field *schema.Field, dst reflect.Value, fieldValue interface{}) (interface{}, error)

Value 实现 serializer interface

type Param added in v1.1.0

type Param struct {
	// Dest 是参数的目标值(用于 OUT/IN OUT 参数)
	Dest interface{}
	// Size 是字符串类型参数的大小(用于 VARCHAR2 等变长类型)
	Size int
	// In 表示是否为 IN 或 IN OUT 参数
	// false: OUT 参数
	// true: IN 或 IN OUT 参数
	In bool
}

Param 是存储过程参数的本地包装结构 用于隐藏底层驱动(当前为 go-ora)的细节,提供统一的 API godror 为路线图项(未接线),暂不涉及切换

func InOutParam added in v1.1.0

func InOutParam(dest interface{}) Param

InOutParam 创建一个 IN OUT 参数 用于存储过程的输入输出参数

func OutParam added in v1.1.0

func OutParam(dest interface{}, size int) Param

OutParam 创建一个 OUT 参数 用于存储过程的输出参数

type RefCursor added in v1.1.0

type RefCursor struct {
	// contains filtered or unexported fields
}

RefCursor 是游标类型的本地包装 用于存储过程返回结果集

func NewRefCursor added in v1.1.0

func NewRefCursor() *RefCursor

NewRefCursor 创建一个新的游标参数

func (*RefCursor) ToDriverCursor added in v1.1.0

func (r *RefCursor) ToDriverCursor() *go_ora.RefCursor

ToDriverCursor 将本地 RefCursor 转换为底层驱动的 RefCursor

Directories

Path Synopsis
Package driver_adapter 提供 Oracle 驱动抽象层。
Package driver_adapter 提供 Oracle 驱动抽象层。

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL