/
tx.go
169 lines (146 loc) · 5.17 KB
/
tx.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
// Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
// This source code is licensed under the Apache 2.0 license found
// in the LICENSE file in the root directory of this source tree.
// Code generated by entc, DO NOT EDIT.
package ent
import (
"context"
"sync"
"github.com/facebookincubator/ent/dialect"
)
// Tx is a transactional client that is created by calling Client.Tx().
type Tx struct {
config
// Card is the client for interacting with the Card builders.
Card *CardClient
// Comment is the client for interacting with the Comment builders.
Comment *CommentClient
// FieldType is the client for interacting with the FieldType builders.
FieldType *FieldTypeClient
// File is the client for interacting with the File builders.
File *FileClient
// FileType is the client for interacting with the FileType builders.
FileType *FileTypeClient
// Group is the client for interacting with the Group builders.
Group *GroupClient
// GroupInfo is the client for interacting with the GroupInfo builders.
GroupInfo *GroupInfoClient
// Item is the client for interacting with the Item builders.
Item *ItemClient
// Node is the client for interacting with the Node builders.
Node *NodeClient
// Pet is the client for interacting with the Pet builders.
Pet *PetClient
// Spec is the client for interacting with the Spec builders.
Spec *SpecClient
// User is the client for interacting with the User builders.
User *UserClient
// lazily loaded.
client *Client
clientOnce sync.Once
// completion callbacks.
mu sync.Mutex
onCommit []func(error)
onRollback []func(error)
}
// Commit commits the transaction.
func (tx *Tx) Commit() error {
err := tx.config.driver.(*txDriver).tx.Commit()
tx.mu.Lock()
defer tx.mu.Unlock()
for _, f := range tx.onCommit {
f(err)
}
return err
}
// OnCommit adds a function to call on commit.
func (tx *Tx) OnCommit(f func(error)) {
tx.mu.Lock()
defer tx.mu.Unlock()
tx.onCommit = append(tx.onCommit, f)
}
// Rollback rollbacks the transaction.
func (tx *Tx) Rollback() error {
err := tx.config.driver.(*txDriver).tx.Rollback()
tx.mu.Lock()
defer tx.mu.Unlock()
for _, f := range tx.onRollback {
f(err)
}
return err
}
// OnRollback adds a function to call on rollback.
func (tx *Tx) OnRollback(f func(error)) {
tx.mu.Lock()
defer tx.mu.Unlock()
tx.onRollback = append(tx.onRollback, f)
}
// Client returns a Client that binds to current transaction.
func (tx *Tx) Client() *Client {
tx.clientOnce.Do(func() {
tx.client = &Client{config: tx.config}
tx.client.init()
})
return tx.client
}
func (tx *Tx) init() {
tx.Card = NewCardClient(tx.config)
tx.Comment = NewCommentClient(tx.config)
tx.FieldType = NewFieldTypeClient(tx.config)
tx.File = NewFileClient(tx.config)
tx.FileType = NewFileTypeClient(tx.config)
tx.Group = NewGroupClient(tx.config)
tx.GroupInfo = NewGroupInfoClient(tx.config)
tx.Item = NewItemClient(tx.config)
tx.Node = NewNodeClient(tx.config)
tx.Pet = NewPetClient(tx.config)
tx.Spec = NewSpecClient(tx.config)
tx.User = NewUserClient(tx.config)
}
// txDriver wraps the given dialect.Tx with a nop dialect.Driver implementation.
// The idea is to support transactions without adding any extra code to the builders.
// When a builder calls to driver.Tx(), it gets the same dialect.Tx instance.
// Commit and Rollback are nop for the internal builders and the user must call one
// of them in order to commit or rollback the transaction.
//
// If a closed transaction is embedded in one of the generated entities, and the entity
// applies a query, for example: Card.QueryXXX(), the query will be executed
// through the driver which created this transaction.
//
// Note that txDriver is not goroutine safe.
type txDriver struct {
// the driver we started the transaction from.
drv dialect.Driver
// tx is the underlying transaction.
tx dialect.Tx
}
// newTx creates a new transactional driver.
func newTx(ctx context.Context, drv dialect.Driver) (*txDriver, error) {
tx, err := drv.Tx(ctx)
if err != nil {
return nil, err
}
return &txDriver{tx: tx, drv: drv}, nil
}
// Tx returns the transaction wrapper (txDriver) to avoid Commit or Rollback calls
// from the internal builders. Should be called only by the internal builders.
func (tx *txDriver) Tx(context.Context) (dialect.Tx, error) { return tx, nil }
// Dialect returns the dialect of the driver we started the transaction from.
func (tx *txDriver) Dialect() string { return tx.drv.Dialect() }
// Close is a nop close.
func (*txDriver) Close() error { return nil }
// Commit is a nop commit for the internal builders.
// User must call `Tx.Commit` in order to commit the transaction.
func (*txDriver) Commit() error { return nil }
// Rollback is a nop rollback for the internal builders.
// User must call `Tx.Rollback` in order to rollback the transaction.
func (*txDriver) Rollback() error { return nil }
// Exec calls tx.Exec.
func (tx *txDriver) Exec(ctx context.Context, query string, args, v interface{}) error {
return tx.tx.Exec(ctx, query, args, v)
}
// Query calls tx.Query.
func (tx *txDriver) Query(ctx context.Context, query string, args, v interface{}) error {
return tx.tx.Query(ctx, query, args, v)
}
var _ dialect.Driver = (*txDriver)(nil)