本项目实现了一个类似 DBeaver 的数据库目录层,为前端提供树形结构的数据库对象导航功能。支持懒加载、分页、搜索、完整的 CRUD 操作以及丰富的事务支持。
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Frontend │ │ Database Layer │ │ MySQL Server │
│ (Wails) │◄──►│ (Go) │◄──►│ │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│
▼
┌─────────────────┐
│ Meta Layer │
│ (Node, Types) │
└─────────────────┘
type NodeReader interface {
GetRoot(ctx context.Context) (*meta.Node, error)
GetChildren(ctx context.Context, nodeID string, limit, offset int) ([]*meta.Node, error)
GetNode(ctx context.Context, nodeID string) (*meta.Node, error)
RefreshNode(ctx context.Context, nodeID string) error
}
type SchemaReader interface {
ListSchemas(ctx context.Context, limit, offset int) ([]*meta.Node, error)
GetSchema(ctx context.Context, schemaName string) (*meta.Node, error)
}
type TableReader interface {
ListTables(ctx context.Context, schemaName string, limit, offset int) ([]*meta.Node, error)
GetTable(ctx context.Context, schemaName, tableName string) (*meta.Node, error)
ListColumns(ctx context.Context, schemaName, tableName string, limit, offset int) ([]*meta.Node, error)
GetColumn(ctx context.Context, schemaName, tableName, columnName string) (*meta.Node, error)
}
type ViewReader interface {
ListViews(ctx context.Context, schemaName string, limit, offset int) ([]*meta.Node, error)
GetView(ctx context.Context, schemaName, viewName string) (*meta.Node, error)
}
type IndexReader interface {
ListIndexes(ctx context.Context, schemaName, tableName string, limit, offset int) ([]*meta.Node, error)
GetIndex(ctx context.Context, schemaName, tableName, indexName string) (*meta.Node, error)
}
type TriggerReader interface {
ListTriggers(ctx context.Context, schemaName, tableName string, limit, offset int) ([]*meta.Node, error)
GetTrigger(ctx context.Context, schemaName, tableName, triggerName string) (*meta.Node, error)
}
type MetadataReader interface {
GetConnectionInfo(ctx context.Context) (*meta.Metadata, error)
SearchNodes(ctx context.Context, keyword string, nodeType meta.NodeType, limit, offset int) ([]*meta.Node, error)
}
type NodeWriter interface {
UpdateNode(ctx context.Context, nodeID string, updates map[string]interface{}, tx ...types.Transaction) error
RenameNode(ctx context.Context, nodeID, newName string, tx ...types.Transaction) error
DeleteNode(ctx context.Context, nodeID string, tx ...types.Transaction) error
CreateNode(ctx context.Context, parentID string, nodeType meta.NodeType, metadata *meta.Metadata, tx ...types.Transaction) (*meta.Node, error)
}
type SchemaWriter interface {
CreateSchema(ctx context.Context, schemaName string, metadata *meta.Metadata, tx ...types.Transaction) (*meta.Node, error)
DropSchema(ctx context.Context, schemaName string, tx ...types.Transaction) error
AlterSchema(ctx context.Context, schemaName string, updates map[string]interface{}, tx ...types.Transaction) error
}
type TableWriter interface {
CreateTable(ctx context.Context, schemaName string, metadata *meta.Metadata, tx ...types.Transaction) (*meta.Node, error)
DropTable(ctx context.Context, schemaName, tableName string, tx ...types.Transaction) error
AlterTable(ctx context.Context, schemaName, tableName string, updates map[string]interface{}, tx ...types.Transaction) error
RenameTable(ctx context.Context, schemaName, oldName, newName string, tx ...types.Transaction) error
}
type ViewWriter interface {
CreateView(ctx context.Context, schemaName string, metadata *meta.Metadata, tx ...types.Transaction) (*meta.Node, error)
DropView(ctx context.Context, schemaName, viewName string, tx ...types.Transaction) error
AlterView(ctx context.Context, schemaName, viewName string, updates map[string]interface{}, tx ...types.Transaction) error
RenameView(ctx context.Context, schemaName, oldName, newName string, tx ...types.Transaction) error
}
type ColumnWriter interface {
AddColumn(ctx context.Context, schemaName, tableName string, metadata *meta.Metadata, tx ...types.Transaction) (*meta.Node, error)
DropColumn(ctx context.Context, schemaName, tableName, columnName string, tx ...types.Transaction) error
AlterColumn(ctx context.Context, schemaName, tableName, columnName string, updates map[string]interface{}, tx ...types.Transaction) error
RenameColumn(ctx context.Context, schemaName, tableName, oldName, newName string, tx ...types.Transaction) error
}
type IndexWriter interface {
CreateIndex(ctx context.Context, schemaName, tableName string, metadata *meta.Metadata, tx ...types.Transaction) (*meta.Node, error)
DropIndex(ctx context.Context, schemaName, tableName, indexName string, tx ...types.Transaction) error
AlterIndex(ctx context.Context, schemaName, tableName, indexName string, updates map[string]interface{}, tx ...types.Transaction) error
}
type TriggerWriter interface {
CreateTrigger(ctx context.Context, schemaName, tableName string, metadata *meta.Metadata, tx ...types.Transaction) (*meta.Node, error)
DropTrigger(ctx context.Context, schemaName, tableName, triggerName string, tx ...types.Transaction) error
AlterTrigger(ctx context.Context, schemaName, tableName, triggerName string, updates map[string]interface{}, tx ...types.Transaction) error
}
type DatabaseReader interface {
NodeReader
SchemaReader
TableReader
ViewReader
IndexReader
TriggerReader
MetadataReader
}
type DatabaseWriter interface {
NodeWriter
SchemaWriter
TableWriter
ViewWriter
ColumnWriter
IndexWriter
TriggerWriter
}
type DatabaseProvider interface {
DatabaseReader
DatabaseWriter
TransactionManager
Connection
Connect(ctx context.Context, config map[string]interface{}) error
Disconnect(ctx context.Context) error
Ping(ctx context.Context) error
}
db_view/
├── service/
│ └── common/
│ └── databases/
│ ├── interface.go # 接口定义
│ ├── meta/ # 元数据层
│ │ ├── meta.go # Node和Metadata定义
│ │ └── types.go # 基础类型定义
│ └── drivers/ # 驱动层
│ └── mysql/
│ └── version/
│ └── v8/ # MySQL 8.0+ 驱动
│ ├── connection/
│ │ └── connection.go # 连接管理
│ ├── navigation/
│ │ ├── node.go # 节点操作
│ │ └── metadata.go # 元数据操作
│ └── operations/
│ ├── schema.go # 模式操作
│ ├── table.go # 表操作
│ ├── view.go # 视图操作
│ ├── index.go # 索引操作
│ ├── trigger.go # 触发器操作
│ └── object.go # 对象操作
// Node 树形结构节点
type Node struct {
ID string // 唯一标识符
Type NodeType // 节点类型
Metadata *Metadata // 元数据
Children []*Node // 子节点
Loaded bool // 是否已加载
ParentID string // 父节点ID
}
// Metadata 节点元数据
type Metadata struct {
Name string // 对象名称
Description string // 描述信息
Extra map[string]interface{} // 扩展属性
}
// 节点按需加载,避免一次性加载大量数据
func (c *MySQLConnection) ListChildren(ctx context.Context, nodeID string, limit, offset int) ([]*meta.Node, error) {
// 只加载当前页的数据
// 支持分页参数 limit 和 offset
}
// 所有写操作都支持可选事务参数
func (c *MySQLConnection) CreateTable(ctx context.Context, schemaName string, metadata *meta.Metadata, tx ...types.Transaction) (*meta.Node, error) {
if len(tx) > 0 {
// 使用传入的事务
return c.createTableInTransaction(ctx, schemaName, metadata, tx[0])
}
// 创建新事务
return c.createTableWithNewTransaction(ctx, schemaName, metadata)
}
// 使用参数化查询防止SQL注入
query := "SELECT * FROM information_schema.tables WHERE table_schema = ? AND table_name = ?"
rows, err := c.db.QueryContext(ctx, query, schemaName, tableName)
// 统一的错误返回模式
if err != nil {
return nil, fmt.Errorf("操作失败: %w", err)
}
// 使用连接池提升性能
db, err := sql.Open("mysql", dsn)
db.SetMaxOpenConns(10)
db.SetMaxIdleConns(5)
db.SetConnMaxLifetime(time.Hour)
// 支持按关键字搜索不同类型的数据库对象
func (c *MySQLConnection) SearchNodes(ctx context.Context, keyword string, nodeType meta.NodeType, limit, offset int) ([]*meta.Node, error) {
switch nodeType {
case meta.NodeTypeSchema:
return c.searchSchemas(ctx, keyword, limit, offset)
case meta.NodeTypeTable:
return c.searchTables(ctx, keyword, limit, offset)
// ... 其他类型
}
}
// 所有列表操作都支持分页
func (c *MySQLConnection) ListTables(ctx context.Context, schemaName string, limit, offset int) ([]*meta.Node, error) {
query := `
SELECT table_name, table_type, create_time
FROM information_schema.tables
WHERE table_schema = ?
LIMIT ? OFFSET ?
`
// 执行分页查询
}
type Transaction interface {
Commit() error
Rollback() error
IsActive() bool
}
type TransactionManager interface {
BeginTransaction(ctx context.Context, opts *types.TransactionOptions) (types.Transaction, error)
ExecuteInTransaction(ctx context.Context, opts *types.TransactionOptions, fn func(txCtx context.Context) error) error
}
type TransactionOptions struct {
IsolationLevel string // 隔离级别
ReadOnly bool // 只读事务
TimeoutSeconds int // 超时时间
}
// 创建连接
conn := &v8.MySQLConnection{}
// 连接数据库
config := map[string]interface{}{
"host": "localhost",
"port": 3306,
"username": "root",
"password": "password",
"database": "test",
}
err := conn.Connect(ctx, config)
// 获取根节点
root, err := conn.GetRoot(ctx)
// 获取子节点(支持分页)
children, err := conn.GetChildren(ctx, "schema:test", 50, 0)
// 获取特定节点
node, err := conn.GetNode(ctx, "table:test.users")
// 搜索表
tables, err := conn.SearchNodes(ctx, "user", meta.NodeTypeTable, 20, 0)
// 搜索所有类型
all, err := conn.SearchNodes(ctx, "test", meta.NodeTypeAll, 50, 0)
// 创建表
metadata := &meta.Metadata{
Name: "new_table",
Extra: map[string]interface{}{
"columns": []map[string]interface{}{
{"name": "id", "type": "INT", "primary_key": true},
{"name": "name", "type": "VARCHAR(255)"},
},
},
}
table, err := conn.CreateTable(ctx, "test", metadata)
// 修改表
updates := map[string]interface{}{
"comment": "Updated table comment",
}
err = conn.AlterTable(ctx, "test", "new_table", updates)
// 删除表
err = conn.DropTable(ctx, "test", "new_table")
// 在事务中执行多个操作
err := conn.ExecuteInTransaction(ctx, nil, func(txCtx context.Context) error {
// 创建表
_, err := conn.CreateTable(txCtx, "test", tableMetadata)
if err != nil {
return err
}
// 添加索引
_, err = conn.CreateIndex(txCtx, "test", "new_table", indexMetadata)
if err != nil {
return err
}
return nil
})
database:
host: localhost
port: 3306
username: root
password: password
database: test
max_open_conns: 10
max_idle_conns: 5
conn_max_lifetime: 1h
本文档详细记录了 MySQL Database Directory Layer 的完整设计和实现方案,为后续开发和维护提供重要参考。
c:\Users\GTong\OneDrive\Code\Go\Work\wails\db_view\DESIGN_DOCUMENT.md