Skip to content

Instantly share code, notes, and snippets.

@tupunco
Last active December 16, 2016 06:57
Show Gist options
  • Save tupunco/20f8928abfdf7bc2cd9f5f4381510953 to your computer and use it in GitHub Desktop.
Save tupunco/20f8928abfdf7bc2cd9f5f4381510953 to your computer and use it in GitHub Desktop.
boltdb demo
package main
import (
"bytes"
"encoding/json"
"errors"
"reflect"
"github.com/boltdb/bolt"
"github.com/timshannon/bolthold"
)
// BoltDB WebUI 工具:
// cd ./data
// boltdbweb -d=db.bolt -p=9000 -s="G:/Work/GoPath/src/github.com/evnix/boltdbweb/"
const (
boltDBFileName = "data/db.bolt"
)
var (
//BoltX BoltDB Store
BoltX *BoltWrap
boltXModels = make(map[string]interface{}) // bolt models map
boltXHasInit bool // bolt models has int
)
func init() {
Registr2Bolt(&BoltIDIncrement{})
}
// initBoltDB 初始化 BoltDB 配置
func initBoltDB() {
BoltX = NewBoltWrap(boltDBFileName)
//INFO: bolthold 会为每个 typeOf(BoltModels) 创建一个 Bucket,
// 对每个 Model 初始化一下, 避免没有对某 Model 进行过 `写` 操作之前 Get 的时候报 bucket nil 错误.
for _, data := range boltXModels {
if err := BoltX.Upsert("-", data); err != nil {
panic(err)
}
}
}
// Registr2Bolt init Registr BoltDB Model
func Registr2Bolt(defaultData interface{}) {
boltXModels[reflect.TypeOf(defaultData).String()] = defaultData
}
// GetBoltStore get bolt store
func GetBoltStore(dbFileName string) (*bolthold.Store, error) {
if len(dbFileName) <= 0 {
return nil, errors.New("dbFileName param nil")
}
//return bolthold.Open(dbFileName, 0666, nil)
jsonOptions := &bolthold.Options{
Encoder: DefaultEncode,
Decoder: DefaultDecode,
}
return bolthold.Open(dbFileName, 0666, jsonOptions)
}
//----------------------BoltWrap----------------------
// BoltWrap bolt wrap
type BoltWrap struct {
*bolthold.Store
dbFileName string
}
// BoltIDIncrement ID Increment
type BoltIDIncrement struct {
Current int64
}
// NewBoltWrap new BoltWrap
func NewBoltWrap(dbFileName string) *BoltWrap {
db, err := GetBoltStore(dbFileName)
if err != nil {
panic("init BoltDB fail")
}
return &BoltWrap{
dbFileName: dbFileName,
Store: db,
}
}
// IsNotFoundError is error notFound
func (b *BoltWrap) IsNotFoundError(err error) bool {
return err == bolthold.ErrNotFound
}
// GeNextBoltID Ge Next BoltID
func (b *BoltWrap) GeNextBoltID(tx *bolt.Tx, key interface{}) (int64, error) {
if tx == nil || key == nil {
return -1, errors.New("tx or key param nil")
}
id := &BoltIDIncrement{}
err := b.TxGet(tx, key, id)
if err != nil && !b.IsNotFoundError(err) {
tx.Rollback()
return -1, err
}
if id.Current <= 0 {
id.Current = 1
if err = b.TxUpsert(tx, key, id); err != nil {
tx.Rollback()
return -1, err
}
} else {
id.Current++
if err = b.TxUpdate(tx, key, id); err != nil {
tx.Rollback()
return -1, err
}
}
return id.Current, nil
}
//-----------BoltDB JSON Encode/tDecode-------------
// DefaultEncode is the default encoding func for bolthold (Gob)
func DefaultEncode(value interface{}) ([]byte, error) {
var buff bytes.Buffer
en := json.NewEncoder(&buff)
err := en.Encode(value)
if err != nil {
return nil, err
}
return buff.Bytes(), nil
}
// DefaultDecode is the default decoding func for bolthold (Gob)
func DefaultDecode(data []byte, value interface{}) error {
var buff bytes.Buffer
de := json.NewDecoder(&buff)
_, err := buff.Write(data)
if err != nil {
return err
}
err = de.Decode(value)
if err != nil {
return err
}
return nil
}
package main
import (
"fmt"
"strings"
)
// ErrDataNotExist 数据不存在 错误
type ErrDataNotExist struct {
Data interface{}
Message string
}
// IsErrDataNotExist Check IS ErrDataNotExist
func IsErrDataNotExist(err error) bool {
_, ok := err.(*ErrDataNotExist)
return ok
}
//Error String
func (err *ErrDataNotExist) Error() string {
return fmt.Sprintf("Data-Not-Exist [Data: %+v, Message: %s]", err.Data, err.Message)
}
//----------------------------------
// ErrParamsError 参数错误
type ErrParamsError struct {
Params []string
}
// NewParamsError new ErrParamsError
func NewParamsError(params ...string) error {
return &ErrParamsError{
Params: params,
}
}
// IsParamsError Check IS ErrParamsError
func IsParamsError(err error) bool {
_, ok := err.(*ErrParamsError)
return ok
}
//Error String
func (err *ErrParamsError) Error() string {
if len(err.Params) <= 0 {
return "param nil"
}
return fmt.Sprintf("params [%s] nil", strings.Join(err.Params, ","))
}
package main
import "fmt"
func main() {
initBoltDB()
addUser, err := AddOrUpdateBoltUserInfo(&User{
OpenID: "21111213-415126",
Nickname: "hell2112o",
})
fmt.Printf("add:%+v-err:%+v\r\n", addUser, err)
addUser, err = GetUserByIDFromBolt(1)
fmt.Printf("get:%+v-err:%+v\r\n", addUser, err)
addUser, err = GetUserByIDFromBolt(2)
fmt.Printf("get:%+v-err:%+v\r\n", addUser, err)
}
package main
import (
"errors"
"fmt"
"github.com/boltdb/bolt"
"github.com/timshannon/bolthold"
)
const (
boltUserKey = "User_ID:%d" //user 表 key
boltUserIDIncrementKey = "User_ID_Increment" //user 表 ID 计数
)
// User 用户基础数据表
type User struct {
ID int64 `json:"id"`
OpenID string `json:"-" boltholdIndex:"OpenID"` // 用户的唯一标识
Nickname string `json:"nickName"` // 用户昵称
}
func init() {
Registr2Bolt(&User{})
}
// GetUserByIDFromBolt GetUserByID returns the user object by given ID if exists.
func GetUserByIDFromBolt(id int64) (*User, error) {
if id <= 0 {
return nil, errors.New("userID nil")
}
u := new(User)
key := fmt.Sprintf(boltUserKey, id)
err := BoltX.Get(key, u)
if err != nil {
if BoltX.IsNotFoundError(err) {
return nil, &ErrDataNotExist{
Data: &struct{ UserID int64 }{id},
}
}
return nil, err
}
return u, nil
}
// GetUserByOpenIDFromBolt Get UserInfo By OpenID
func GetUserByOpenIDFromBolt(openID string) (*User, error) {
if len(openID) <= 0 {
return nil, errors.New("openID nil")
}
var result []*User
err := BoltX.Find(&result, bolthold.Where("OpenID").Eq(openID))
if BoltX.IsNotFoundError(err) || len(result) <= 0 {
return nil, &ErrDataNotExist{
Data: &struct{ OpenID string }{openID},
}
}
if err != nil {
return nil, err
}
u := result[0]
return u, nil
}
// AddOrUpdateBoltUserInfo 添加或者更新用户
func AddOrUpdateBoltUserInfo(u *User) (*User, error) {
if len(u.OpenID) <= 0 {
return nil, NewParamsError("OpenID")
}
var err error
var has bool
user := new(User)
user, err = GetUserByOpenIDFromBolt(u.OpenID)
if err != nil && !IsErrDataNotExist(err) {
return nil, err
}
if user != nil && user.ID > 0 {
has = true
}
bx := BoltX
var tx *bolt.Tx
if tx, err = bx.Bolt().Begin(true); err != nil {
return nil, err
}
if has { //更新用户资料
key := fmt.Sprintf(boltUserKey, user.ID)
user.Nickname = u.Nickname
if err = bx.TxUpdate(tx, key, user); err != nil {
tx.Rollback()
return nil, err
}
} else { //添加新用户
// 生成新的 UserID
var nextID int64
if nextID, err = bx.GeNextBoltID(tx, boltUserIDIncrementKey); err != nil {
tx.Rollback()
return nil, err
}
key := fmt.Sprintf(boltUserKey, nextID)
user = &User{
ID: nextID,
OpenID: u.OpenID,
Nickname: u.Nickname,
}
if err := bx.TxInsert(tx, key, user); err != nil {
tx.Rollback()
return nil, err
}
}
if err = tx.Commit(); err != nil {
return nil, err
}
return user, nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment