Golang结构体数据循环问题解析

Golang结构体数据循环问题解析 在我的代码中,我在 init 函数中执行以下代码块:

package main

import (
	"syscall/js"
)

type object map[string]interface{}

var Window = js.Global()

type DataBase struct {
	Data, Transactions, ObjectStore, Request js.Value
	Upgrade                                  js.Func
}

var DB DataBase // 所有结构体字段都初始化为它们的零值

func init() {
	DB.init("dataSet", "table")
}

其中 DB.init 函数是:

package main

import (
	"fmt"
	"syscall/js" 
)

func (db *DataBase) init(dataSet, table string) {
	var Ok, Err js.Func

	db.Request = Window.Get("indexedDB").Call("open", dataSet, 1)

	db.Upgrade = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		defer db.Upgrade.Release()
		db.Data = this.Get("result")
		Store := db.Data.Call("createObjectStore", table, map[string]interface{}{"keyPath": "id"}) 
		Store.Call("add", map[string]interface{}{"id": "00-03", "name": "Karam", "age": 19, "email": "kenny@planet.org"})

		Window.Call("alert", "第一条记录已发布。")
		return nil
	})

	Ok = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		db.Data = this.Get("result")
		stores := []string{table} 
		objectStore := make([]interface{}, len(stores))
		for i, v := range stores {
			objectStore[i] = v
		}

		db.Transactions = db.Data.Call("transaction", objectStore, "readwrite").Call("objectStore", table)
		db.ObjectStore = db.Data.Call("transaction", objectStore).Call("objectStore", table)

		Window.Call("alert", "数据库已准备就绪!")
		return nil
	})

	Err = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		Window.Call("alert", "抱歉,无法初始化数据库")
		return nil
	})

	db.Request.Set("onupgradeneeded", db.Upgrade)
	db.Request.Set("onsuccess", Ok)
	db.Request.Set("onerror", Err)
}

以上代码使用了 indexedDB API,其中 db.Upgrade 仅在应用程序首次运行时执行一次(或者如果数据库已被删除,或者分配了新的版本号)。

然后,每次应用程序运行时(在升级之后),Ok 函数都会作为第一件事运行。

根据上面的代码,我假设只要应用程序在运行,变量 db.Transactions 就应该存在并附加到 DB 上,并且我应该能够通过附加到结构体的任何方法来调用它。

但是,当我在后续阶段调用 Add 函数时,即:

func (db *DataBase) Add(table string, usr map[string]interface{}) {
	fmt.Println(usr)
	var Ok, Err js.Func
	fmt.Println(table)
	stores := []string{table} // 为了解决意外的字面量 'employee',期望方法或接口名称
	objectStore := make([]interface{}, len(stores))
	for i, v := range stores {
		objectStore[i] = v
	}

	// db.Transactions = db.Data.Call("transaction", objectStore, "readwrite").Call("objectStore", table)

	request := db.Transactions.Call("add", usr)

	Ok = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		defer Ok.Release()
		Window.Call("alert", "用户已添加。")
		return nil
	})
	request.Set("onsuccess", Ok)

	Err = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		defer Err.Release()

		Window.Call("alert", "抱歉,无法添加用户//。")
		return nil
	})
	request.Set("onerror", Err)
}

我得到一个关于 db.Transactions 的错误:

panic: JavaScript error: Failed to execute 'add' on 'IDBObjectStore': The transaction has finished.

但是,如果我在 Add 函数的代码中重新定义它,即取消注释这行:

// db.Transactions = db.Data.Call("transaction", objectStore, "readwrite").Call("objectStore", table)

那么一切都会顺利运行!

有什么想法吗?


更多关于Golang结构体数据循环问题解析的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于Golang结构体数据循环问题解析的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


问题出在IndexedDB事务的生命周期管理上。在Go WebAssembly中,IndexedDB事务是自动提交的,一旦回调函数执行完毕,事务就会自动结束。

在你的代码中,init函数里的Ok回调创建了一个事务,但这个事务在回调执行完成后就自动结束了。所以后续调用Add方法时,db.Transactions引用的是一个已经结束的事务。

正确的做法是每次需要操作数据库时都创建新的事务。以下是修改后的Add函数:

func (db *DataBase) Add(table string, usr map[string]interface{}) {
    fmt.Println(usr)
    var Ok, Err js.Func
    
    // 每次操作都需要创建新的事务
    stores := []string{table}
    objectStore := make([]interface{}, len(stores))
    for i, v := range stores {
        objectStore[i] = v
    }
    
    // 创建新的事务
    transaction := db.Data.Call("transaction", objectStore, "readwrite")
    objectStoreHandle := transaction.Call("objectStore", table)
    
    request := objectStoreHandle.Call("add", usr)

    Ok = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
        defer Ok.Release()
        Window.Call("alert", "用户已添加。")
        return nil
    })
    request.Set("onsuccess", Ok)

    Err = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
        defer Err.Release()
        Window.Call("alert", "抱歉,无法添加用户。")
        return nil
    })
    request.Set("onerror", Err)
}

同样,如果你需要其他数据库操作(如查询、更新、删除),也应该遵循相同的模式:

func (db *DataBase) Get(table string, key string) {
    stores := []string{table}
    objectStore := make([]interface{}, len(stores))
    for i, v := range stores {
        objectStore[i] = v
    }
    
    transaction := db.Data.Call("transaction", objectStore, "readonly")
    objectStoreHandle := transaction.Call("objectStore", table)
    
    request := objectStoreHandle.Call("get", key)
    
    request.Set("onsuccess", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
        result := args[0].Get("target").Get("result")
        fmt.Println("查询结果:", result)
        return nil
    }))
}

对于init函数中的Ok回调,可以只保留数据库连接,而不创建长期事务:

Ok = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
    db.Data = this.Get("result")
    Window.Call("alert", "数据库已准备就绪!")
    return nil
})

这样修改后,每次数据库操作都会使用独立的事务,避免了事务已结束的错误。

回到顶部