-
Notifications
You must be signed in to change notification settings - Fork 13
/
sql_01.go
1596 lines (1348 loc) · 64.5 KB
/
sql_01.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
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
package main
import (
"context"
"database/sql"
"database/sql/driver"
"fmt"
"github.com/go-sql-driver/mysql"
_ "github.com/go-sql-driver/mysql" //一定要导入这个,注册驱动的意思
"strconv"
"time"
//记得在开始本文件的执行之前需要确保github.com/go-sql-driver/mysql里面的文件没被你手动更改过
)
func main() {
db, err := sql.Open("mysql", "root:mysql@/godb")
if err != nil {
panic(err.Error())
}
defer db.Close()
err = db.Ping()
if err != nil {
panic(err.Error())
}
rows, err := db.Query("select * from person")
check_err_sql(err)
PirntQueryRows(rows)
fmt.Println("-------------------------------------")
// Drivers returns a sorted list of the names of the registered drivers.(//Drivers返回已注册驱动程序名称的排序列表。)
fmt.Println(sql.Drivers())
//// A NamedArg is a named argument. NamedArg values may be used as
//// arguments to Query or Exec and bind to the corresponding named
//// parameter in the SQL statement.
////
//// For a more concise way to create NamedArg values, see
//// the Named function.
//// NamedArg是一个命名参数。 NamedArg值可用作Query或Exec的参数,并绑定到SQL语句中相应的命名参数。
////有关创建NamedArg值的更简洁方法,请参见Named函数。
//type NamedArg struct {
// _Named_Fields_Required struct{}
//
// // Name is the name of the parameter placeholder.
// //
// // If empty, the ordinal position in the argument list will be
// // used.
// //
// // Name must omit any symbol prefix.
// // Name是参数占位符的名称。
// //如果Name为空,将使用参数列表中的序数位置。
// //Name必须省略任何符号前缀
// Name string
//
// // Value is the value of the parameter.
// // It may be assigned the same value types as the query
// // arguments.
// // Value是参数的值。
// //可以为其分配与查询参数相同的值类型。
// Value interface{}
//}
// Named provides a more concise way to create NamedArg values.
// Named提供了一种更简洁的方法来创建NamedArg值。
// Example usage:
//
// db.ExecContext(ctx, `
// delete from Invoice
// where
// TimeCreated < @end
// and TimeCreated >= @start;`,
// sql.Named("start", startTime),
// sql.Named("end", endTime),
// )
namedArg := sql.Named("name", "anko")
fmt.Printf("%+v\n",namedArg)
//不知道为什么只能填充一个问号
//rows, err = db.Query("select * from person where id=? and id=?",2,1)
rows, err = db.Query("select * from person where id=?",2)
//go-sql-driver\mysql这个驱动不支持这种写法
//输出:mysql: driver does not support the use of Named Parameters
check_err_sql(err)
PirntQueryRows(rows)
//输出:
// [mysql]
// {_Named_Fields_Required:{} Name:name Value:anko}
// mysql: driver does not support the use of Named Parameters
fmt.Println("---------------从连接池中拿出来一个连接进行操作----------------------")
//// Conn represents a single database connection rather than a pool of database
//// connections. Prefer running queries from DB unless there is a specific
//// need for a continuous single database connection.
////
//// A Conn must call Close to return the connection to the database pool
//// and may do so concurrently with a running query.
////
//// After a call to Close, all operations on the
//// connection fail with ErrConnDone.
//// Conn表示单个数据库连接,而不是数据库连接池。 除非特别需要连续的单个数据库连接,否则最好从DB运行查询。
//// Conn必须调用Close才能返回到数据库池的连接,并且可以与正在运行的查询同时进行。
////调用Close之后,对该连接的所有操作都会因ErrConnDone失败。
//type Conn struct {
// DB是表示零个或多个已经存在的连接池数据库句柄。 对于多个goroutine并发使用是安全的。
// sql软件包自动创建并释放连接; 它还维护空闲连接的空闲池。 如果数据库具有按连接状态的概念,则可以在事务(Tx)或连接(Conn)中可靠地观察到这种状态。
// 调用DB.Begin之后,返回的Tx将绑定到单个连接。 在事务上调用Commit或Rollback后,该事务的连接将返回到DB的空闲连接池。 池大小可以使用SetMaxIdleConns控制。
// 说白了就是连接池句柄
// db *DB
//
// // closemu prevents the connection from closing while there
// // is an active query. It is held for read during queries
// // and exclusively during close.
// // closemu防止在有活动查询时关闭连接。 只在连接查询queries期间和关闭连接close期间上锁。
// closemu sync.RWMutex
//
// // dc is owned until close, at which point
// // it's returned to the connection pool.
// // 拥有驱动连接dc,直到关闭为止,这时它将返回到连接池。代表增删改操作附属在哪个连接上进行
// dc *driverConn
//
// // done transitions from 0 to 1 exactly once, on close.
// // Once done, all operations fail with ErrConnDone.
// // Use atomic operations on value when checking value.
// // DONE在CLOSE上从0到1的转换仅一次。
// //一旦完成,所有操作都会因ErrConnDone失败。
// //检查值时对值使用原子操作。
// done int32
//}
// Conn returns a single connection by either opening a new connection
// or returning an existing connection from the connection pool. Conn will
// block until either a connection is returned or ctx is canceled.
// Queries run on the same Conn will be run in the same database session.
//
// Every Conn must be returned to the database pool after use by
// calling Conn.Close.
// Conn通过打开新连接或从连接池返回现有连接来返回单个连接。 Conn将阻塞,直到返回连接或取消ctx。
// 在同一Conn上运行的查询将在同一数据库会话中运行。
// 使用后,必须通过调用Conn.Close将每个Conn返回到数据库池。
// 说白了就是从连接池中get拿到一个连接对象出来,操作完后要close()来put放回连接池中去
conn, err := db.Conn(context.Background())
check_err_sql(err)
background := context.Background()
rows, err = conn.QueryContext(background, "select * from person")
check_err_sql(err)
PirntQueryRows(rows)
// QueryRowContext executes a query that is expected to return at most one row.
// QueryRowContext always returns a non-nil value. Errors are deferred until
// Row's Scan method is called.
// If the query selects no rows, the *Row's Scan will return ErrNoRows.
// Otherwise, the *Row's Scan scans the first selected row and discards
// the rest.
// QueryRowContext执行的查询预期最多返回一行。
// QueryRowContext始终返回非null值。 错误将一直延迟到调用Row的Scan方法。
//如果查询未返回任何行row,则“行扫描*Row's Scan”将返回ErrNoRows。
//否则,*行扫描*Row's Scan将扫描所选的第一行,并丢弃其余的行。
row := conn.QueryRowContext(background, "select * from person")
check_err_sql(err)
PirntQueryRow(row)
// Close returns the connection to the connection pool.
// All operations after a Close will return with ErrConnDone.
// Close is safe to call concurrently with other operations and will
// block until all other operations finish. It may be useful to first
// cancel any used context and then call close directly after.
// Close将连接返回到连接池。
// Close后的所有操作都将返回ErrConnDone。
// Close可以安全地与其他操作同时调用,并且将阻塞直到所有其他操作完成。 最好首先取消任何已经在使用的上下文,然后再直接调用close。
defer func() {
//fmt.Println(background.Done())//这个上下文无法取消的!因为这个上下文一直没创建!是个占位的上下文而已
conn.Close()
}()
// PrepareContext creates a prepared statement for later queries or executions.
// Multiple queries or executions may be run concurrently from the
// returned statement.
// The caller must call the statement's Close method
// when the statement is no longer needed.
//
// The provided context is used for the preparation of the statement, not for the
// execution of the statement.
// PrepareContext为以后的查询或执行创建准备好的语句。
//可以从返回的语句statement中同时运行多个查询或执行。
//当不再需要该语句statement时,调用者必须调用该语句的Close方法。
//提供的上下文用于语句statement的准备,而不是语句的执行。
//下面的stmt是statement的简写,为什么要生成语句statement呢?
//sql.Stmt支持预备表达式,可以用来优化SQL查询提高性能,减少SQL注入的风险, DB.Prepare()和Tx.Prepare()都提供了对于预备表达式的支持。
//
//预处理的流程:
// step1. 将sql分为2部分.命令部分和数据部分.
// step2. 首先将命令部分发送给mysql服务器,mysql进行预处理.(如生成AST)
// step3. 然后将数据部分发送给mysql服务器,mysql进行占位符替换.
// step4. mysql服务器执行sql语句,把执行结果发送给客户端.
//
//预处理的优势:
// 1.因为发送命令后,在mysql服务器端,就会将AST生成好,所以不需要对每一次值的更换都重新生成一次AST.对同样的数据不同的SQL来讲,只需生成1次AST,并缓存起来即可.
// 2.避免SQL注入.因为mysql知道再次发送过来的内容为”数据”,因此不会将这些数据解析为SQL,避免了SQL注入.
//
//需要注意的点:
// 使用预处理进行查询操作时,不仅在defer时需要关闭结果集,而且还要关闭命令句柄,否则同样会占用连接,导致阻塞.
//// Stmt is a prepared statement.
//// A Stmt is safe for concurrent use by multiple goroutines.
////
//// If a Stmt is prepared on a Tx or Conn, it will be bound to a single
//// underlying connection forever. If the Tx or Conn closes, the Stmt will
//// become unusable and all operations will return an error.
//// If a Stmt is prepared on a DB, it will remain usable for the lifetime of the
//// DB. When the Stmt needs to execute on a new underlying connection, it will
//// prepare itself on the new connection automatically.
//// Stmt是一个准备好的语句statement。
//// 一个Stmt可安全地供多个goroutine并发使用。
//// 如果在Tx或Conn上prepared准备了Stmt,它将永远绑定到单个基础连接。 如果Tx或Conn关闭,则Stmt将变得不可用,并且所有操作都将返回错误。
//// 如果在数据库DB上prepared准备了Stmt,它将在数据库DB的整个生命周期内保持可用状态。 当Stmt需要在新的基础连接上执行时,它将自动在新的连接上进行准备。
//type Stmt struct {
// // Immutable:(//不可变:)
// db *DB // where we came from(//我们来自哪里,指明附属者)
// query string // that created the Stmt(//创造了Stmt的操作字符串)
// stickyErr error // if non-nil, this error is returned for all operations(//如果非nil,则为所有操作返回此错误)
//
// closemu sync.RWMutex // held exclusively during close, for read otherwise.(//仅在关闭期间上锁,其他时候都不会上锁且允许被获取锁)
//
// // If Stmt is prepared on a Tx or Conn then cg is present and will
// // only ever grab a connection from cg.
// // If cg is nil then the Stmt must grab an arbitrary connection
// // from db and determine if it must prepare the stmt again by
// // inspecting css.
// //如果在Tx或Conn上准备了Stmt,则cg被赋值(不为nil),并且当前只会从cg获取连接。
// //如果cg为nil,则Stmt必须从db获取任一连接,并通过检查css确定是否必须再次准备stmt。
// cg stmtConnGrabber
// cgds *driverStmt
//
// // parentStmt is set when a transaction-specific statement
// // is requested from an identical statement prepared on the same
// // conn. parentStmt is used to track the dependency of this statement
// // on its originating ("parent") statement so that parentStmt may
// // be closed by the user without them having to know whether or not
// // any transactions are still using it.
// // 当从同一conn上准备的同一语句statement中请求特定事务的语句statement时,将设置parentStmt。
// // parentStmt用于跟踪此语句statement对其原始(“父”)语句statement的依赖性,以便用户可以关闭parentStmt,而不必知道是否有任何事务在使用它。
// parentStmt *Stmt
//
// mu sync.Mutex // protects the rest of the fields(//保护其余字段)
// closed bool //此语句关闭与否
//
// // css is a list of underlying driver statement interfaces
// // that are valid on particular connections. This is only
// // used if cg == nil and one is found that has idle
// // connections. If cg != nil, cgds is always used.
// // css是在特定连接上有效的基础驱动程序语句接口的列表。 仅当cg == nil并且发现有空闲连接时使用。 如果cg!= nil,则始终使用cgds。
// css []connStmt
//
// // lastNumClosed is copied from db.numClosed when Stmt is created
// // without tx and closed connections in css are removed.
// // 创建不带tx的Stmt并从CSS中关闭的连接删除时,从db.numClosed复制lastNumClosed。
// lastNumClosed uint64
//}
fmt.Println("******")
stmt,err := conn.PrepareContext(background,"select * from person where id=? or name=?")
check_err_sql(err)
fmt.Printf("%+v\n",stmt)
id := 2
name:="anko"
// Query executes a prepared query statement with the given arguments
// and returns the query results as a *Rows.
//Query使用给定的参数执行准备好的查询语句,并将查询结果作为* Rows返回。
//rows, err = stmt.Query(name, id)//顺序千万不能反了,否则什么错误都不会返回
rows, err = stmt.Query(id, name)
check_err_sql(err)
PirntQueryRows(rows)
fmt.Println("******")
rows, err = stmt.QueryContext(background,id, name)
check_err_sql(err)
PirntQueryRows(rows)
fmt.Println("******")
// QueryRowContext executes a prepared query statement with the given arguments.
// If an error occurs during the execution of the statement, that error will
// be returned by a call to Scan on the returned *Row, which is always non-nil.
// If the query selects no rows, the *Row's Scan will return ErrNoRows.
// Otherwise, the *Row's Scan scans the first selected row and discards
// the rest.
// QueryRowContext使用给定的参数执行准备好的查询语句。
//如果在执行语句期间发生错误,则该错误将通过在返回的* Row上调用Scan来返回,该行始终为nil。
//如果查询未选择任何行,则“行扫描”将返回ErrNoRows。
//否则,*行扫描将扫描所选的第一行,并丢弃其余的行。
row= stmt.QueryRowContext(background, id, name)
PirntQueryRow(row)
// QueryRow executes a prepared query statement with the given arguments.
// If an error occurs during the execution of the statement, that error will
// be returned by a call to Scan on the returned *Row, which is always non-nil.
// If the query selects no rows, the *Row's Scan will return ErrNoRows.
// Otherwise, the *Row's Scan scans the first selected row and discards
// the rest.
//
// Example usage:
//
// var name string
// err := nameByUseridStmt.QueryRow(id).Scan(&name)
// QueryRow使用给定的参数执行准备好的查询语句。
//如果在执行语句期间发生错误,则该错误将通过在返回的* Row上调用Scan来返回,该行始终为nil。
//如果查询未选择任何行,则“行扫描”将返回ErrNoRows。
//否则,*行扫描将扫描所选的第一行,并丢弃其余的行。
//
//示例用法:
//
// var name string
// err := nameByUseridStmt.QueryRow(id).Scan(&name)
fmt.Println("******")
row= stmt.QueryRow(id, name)
PirntQueryRow(row)
//输出:
//{1 anko 20}
//{2 gogo 32}
//{1 anko 20}
//******
//&{db:0xc000094000
// query:select * from person where id=? or name=?
// stickyErr:<nil>
// closemu:{w:{state:0 sema:0} writerSem:0 readerSem:0 readerCount:0 readerWait:0}
// cg:0xc0000c2000
// cgds:0xc0000d4000
// parentStmt:<nil>
// mu:{state:0 sema:0}
// closed:false
// css:[]
// lastNumClosed:0}
//{1 anko 20}
//{2 gogo 32}
//******
//{1 anko 20}
//{2 gogo 32}
//&{db:0xc000094000
// query:select * from person where id=? or name=?
// stickyErr:<nil>
// closemu:{w:{state:0 sema:0} writerSem:0 readerSem:0 readerCount:0 readerWait:0}
// cg:0xc0000b2120
// cgds:<nil>
// parentStmt:<nil>
// mu:{state:0 sema:0}
// closed:true 这里跟上面会形成对比
// css:[]
// lastNumClosed:0}
// ******
// {1 anko 20}
// ******
// {1 anko 20} 说明返回的数据是有序的!
fmt.Println("========")
//// A Result summarizes an executed SQL command.(//结果汇总了已执行(准确来说是“已整合”,而不是执行)的SQL命令。)
//type Result interface {
// // LastInsertId returns the integer generated by the database
// // in response to a command. Typically this will be from an
// // "auto increment" column when inserting a new row. Not all
// // databases support this feature, and the syntax of such
// // statements varies.
// // LastInsertId返回数据库为响应命令而生成的整数。 通常,这将在插入新行时来自“自动增量”列数(代表第几行,而不是影响了多少行或者列,下同)。
// // 并非所有数据库都支持此功能,并且此类语句的语法也有所不同。
// // 注意这个操作仅限insert操作才不会为0
// LastInsertId() (int64, error)
//
// // RowsAffected returns the number of rows affected by an
// // update, insert, or delete. Not every database or database
// // driver may support this.
// // RowsAffected返回受更新,插入或删除影响的行数(这里跟上面的列数不同,这里指的是受影响的行数,而不是第几行或者第几列)。 并非每个数据库或数据库驱动程序都可以支持此功能。
// RowsAffected() (int64, error)
//}
//// Exec executes a prepared statement with the given arguments and
//// returns a Result summarizing the effect of the statement.
//// Exec用给定的参数执行一个准备好的语句,并返回一个受到该总结性语句影响的结果Result对象(该接口包含执行sql命令后
//// 的受影响的行数和列数,如果是查询的sql语句的话则不会产生影响数据库的行为,即返回影响的行数和列数都为0,但是如果执行的语句是
//// 增删改sql,则返回的影响的行数和列数均不为0)。
//stmt,err = conn.PrepareContext(background,"insert into person(name,age) values(?,?)")
//check_err_sql(err)
//fmt.Printf("%+v\n",stmt)
//
//Sperson := []struct {
// name string
// age int
//}{
// {"cpp", 23},
// {"java", 24},
// {"go", 23},
// {"python", 56},
//}
//
//
//for id, person := range Sperson {
// fmt.Printf("---%v---",id)
// result, err := stmt.Exec(person.name, person.age)
// check_err_sql(err)
// fmt.Println(result)
// fmt.Println(result.LastInsertId())
// fmt.Println(result.RowsAffected())
//}
//
////数据库中的数据如下:
//// mysql> use godb
//// Database changed
//// mysql> show tables;
//// +----------------+
//// | Tables_in_godb |
//// +----------------+
//// | person |
//// +----------------+
//// 1 row in set (0.01 sec)
////
//// mysql> select * from person;
//// +----+--------+-----+
//// | id | name | age |
//// +----+--------+-----+
//// | 1 | anko | 20 |
//// | 2 | gogo | 32 |,这2行原本就有的!下面才是新添加的!
//// | 3 | cpp | 23 |
//// | 4 | java | 24 |
//// | 5 | go | 23 |
//// | 6 | python | 56 |
//// +----+--------+-----+
//// 6 rows in set (0.00 sec)
//输出如下:
// ========
// &{db:0xc000094000
// query:insert into person(name,age) values(?,?)
// stickyErr:<nil>
// closemu:{w:{state:0 sema:0} writerSem:0 readerSem:0 readerCount:0 readerWait:0}
// cg:0xc0000b20f0
// cgds:0xc0000dc040
// parentStmt:<nil>
// mu:{state:0 sema:0}
// closed:false
// css:[]
// lastNumClosed:0}
// ---0---{0xc0000b0000 0xc0000ce2b0}
// 3 <nil>
// 1 <nil>
// ---1---{0xc0000b0000 0xc0000ce2e0}
// 4 <nil>
// 1 <nil>
// ---2---{0xc0000b0000 0xc0000ce320}
// 5 <nil>
// 1 <nil>
// ---3---{0xc0000b0000 0xc0000ce350}
// 6 <nil>
// 1 <nil>
//在开始之前记得将上面的插入操作代码全部注释掉,否则会再次插入
//不允许插入2行,不允许sql语句中有;号
//stmt,err = conn.PrepareContext(background,`insert into person(name,age) values(?,?);insert into person(name,age) values(?,?)`)
//stmt,err = conn.PrepareContext(background,`UPDATE person SET name = ?, age = ? WHERE id = ?`)//语句1
stmt,err = conn.PrepareContext(background,`UPDATE person SET name = ?, age = ? WHERE id = ? or id = ?`)//语句2
check_err_sql(err)
fmt.Printf("%+v\n",stmt)
result, err := stmt.Exec("Zhongshan", 44,4,5)
check_err_sql(err)
fmt.Println(result)
fmt.Println(result.LastInsertId())
fmt.Println(result.RowsAffected())
//语句1输出:
// &{db:0xc000094000 query:UPDATE person SET name = ?, age = ? WHERE id = ? stickyErr:<nil> closemu:{w:{state:0 sema:0} writerSem:0 readerSem:0 readerCount:0 readerWait:0} cg:0xc0000b2120 cgds:0xc0000be080 parentStmt:<nil> mu:{state:0 sema:0} closed:false css:[] lastNumClosed:0}
// {0xc00009c100 0xc0000ae350}
// 0 <nil>
// 1 <nil>
//语句2输出:
// &{db:0xc0000ae000 query:UPDATE person SET name = ?, age = ? WHERE id = ? or id = ? stickyErr:<nil> closemu:{w:{state:0 sema:0} writerSem:0 readerSem:0 readerCount:0 readerWait:0} cg:0xc00007a930 cgds:0xc000048100 parentStmt:<nil> mu:{state:0 sema:0} closed:false css:[] lastNumClosed:0}
// {0xc0000b2100 0xc000058580}
// 0 <nil>
// 2 <nil>
//特别注意的是,当数据已经是name=Zhongshan,age=44的时候再进行上面update操作的话不会更改任何东西,相应的行列数会返回如下:
// 0 <nil>
// 0 <nil>
//此时数据库数据如下:
// mysql> select * from person;
// +----+-----------+-----+
// | id | name | age |
// +----+-----------+-----+
// | 1 | anko | 20 |
// | 2 | gogo | 32 |
// | 3 | cpp | 23 |
// | 4 | Zhongshan | 44 |
// | 5 | Zhongshan | 44 |
// | 6 | Zhongshan | 44 |
// +----+-----------+-----+
// 6 rows in set (0.00 sec)
//stmt.ExecContext()这个方法跟上面几乎一样,不再累叙
fmt.Println("---------------从连接池中拿出来一个连接进行操作----------------------")
// ExecContext executes a query without returning any rows.
// The args are for any placeholder parameters in the query.
// ExecContext执行查询而不返回任何行。
// args用于查询中的任何占位符参数。
// 返回一个影响的行列数result对象
result,err = conn.ExecContext(background,`UPDATE person SET name = ? WHERE id = ? or id = ?`,"python",4,5)//语句2
check_err_sql(err)
fmt.Println(result)
fmt.Println(result.LastInsertId())
fmt.Println(result.RowsAffected())
//操作后的数据库为:
// mysql> select * from person;
// +----+-----------+-----+
// | id | name | age |
// +----+-----------+-----+
// | 1 | anko | 20 |
// | 2 | gogo | 32 |
// | 3 | cpp | 23 |
// | 4 | python | 44 |
// | 5 | python | 44 |
// | 6 | Zhongshan | 44 |
// +----+-----------+-----+
// 6 rows in set (0.00 sec)
//控制台输出:
// {0xc0000cc080 0xc0000d8350}
// 0 <nil>
// 2 <nil>
// QueryContext执行一个查询,该查询返回行,通常是SELECT。
// args用于查询中的任何占位符参数。
rows, err = conn.QueryContext(background, "select * from person where id=? or id=?", 4, 5)
check_err_sql(err)
PirntQueryRows(rows)
//输出:
// {4 python 44}
// {5 python 44}
// QueryRowContext执行的查询预期最多返回一行。
// QueryRowContext始终返回非null值。 错误将一直延迟到调用Row的Scan方法。
//如果查询未选择任何行,则“行扫描”将返回ErrNoRows。
//否则,*行扫描将扫描所选的第一行,并丢弃其余的行。
//底层其实也是调用了 conn.QueryContext(),然后返回&Row{rows: rows, err: err}即可
row= conn.QueryRowContext(background, "select * from person where id=? or id=?", 4, 5)
PirntQueryRow(row)
//输出:
// {4 python 44}
fmt.Println("----------")
//// driverConn wraps a driver.Conn with a mutex, to
//// be held during all calls into the Conn. (including any calls onto
//// interfaces returned via that Conn, such as calls on Tx, Stmt,
//// Result, Rows)
//// driverConn在要调用Conn的所有过程中用一个互斥量mutex包装driver.Conn(包括通过该Conn返回的接口的任何调用,例如Tx,Stmt,Result,Row的调用)
//type driverConn struct {
// db *DB //连接池对象
// createdAt time.Time //创建时间
//
// sync.Mutex // guards following (//保护以下的数据(上锁,确保同一时刻只有一个g程操作这个driverConn对象))
// ci driver.Conn //被包装的driver.Conn对象
// closed bool //连接关闭与否
// finalClosed bool // ci.Close has been called(//driver.Conn.Close是否被调用了)
// openStmt map[*driverStmt]bool //开放的允许被执行的Stmt语句对象的map
// lastErr error // lastError captures the result of the session resetter.(// lastError捕获会话重置器的结果。表示最近出现的错误)
//
// // guarded by db.mu (//通过db.mu保证以下的数据上锁)
// inUse bool //当前连接ci正在使用与否
// onPut []func() // code (with db.mu held) run when conn is next returned(//下次返回conn时,运行db.mu的代码,相当于回调)
// dbmuClosed bool // same as closed, but guarded by db.mu, for removeClosedStmtLocked(//与关闭相同,但受db.mu保护,用于已经关闭的stmt语句的锁removeClosedStmtLocked)
//}
//// Conn is a connection to a database. It is not used concurrently
//// by multiple goroutines.
////
//// Conn is assumed to be stateful.
//// Conn是到数据库的连接。 多个goroutine不能同时使用它。
////假设Conn是有状态的。
//type Conn interface {
// // Prepare returns a prepared statement, bound to this connection.
// // Prepare返回绑定到此连接的一条预处理语句Stmt。
// Prepare(query string) (Stmt, error)
//
// // Close invalidates and potentially stops any current
// // prepared statements and transactions, marking this
// // connection as no longer in use.
// //
// // Because the sql package maintains a free pool of
// // connections and only calls Close when there's a surplus of
// // idle connections, it shouldn't be necessary for drivers to
// // do their own connection caching.
// // Close使该连接无效并有可能停止任何当前准备的语句和事务,从而将该连接标记为不再使用。
// //因为sql程序包维护一个空闲的连接池,并且仅在有多余的空闲连接时才调用Close,所以驱动程序不必自己进行连接缓存。
// Close() error
//
// // Begin starts and returns a new transaction.
// //
// // Deprecated: Drivers should implement ConnBeginTx instead (or additionally).
// //Begin开始并返回一个新的事务。
// //不推荐使用:驱动程序应改为(或另外)实现ConnBeginTx。
// Begin() (Tx, error)
//}
// Raw executes f exposing the underlying driver connection for the
// duration of f. The driverConn must not be used outside of f.
//
// Once f returns and err is nil, the Conn will continue to be usable
// until Conn.Close is called.
// Raw执行f,在f函数执行的这段持续时间内才暴露公开基础驱动程序连接driverConn(即拥有f的第一个参数ci字段的结构体,同时他是conn的dc字段)。
// 不能在f之外使用driverConn。Raw()方法结束 后会关闭并且释放这个driverConn
// 一旦f返回且err为nil,则Conn将继续可用,直到调用Conn.Close。如果f中抛出异常,则该driverConn会被关闭,因为driverConn封装了Conn,也就是Conn将不可用
err=conn.Raw(func(driverConn interface{}) error{
dc := driverConn.(driver.Conn)
stmt, e := dc.Prepare("select * from person where id =? or id=?")
//除了dc.Prepare,还有dc.Begin(),我不打算在这里讲这个方法,下面会讲到
check_err_sql(e)
if e !=nil{
panic(e)
}
//下面不能为int类型,必须是int64
//仅支持以下类型才可以转化为driver.Value类型:
// int64
// float64
// bool
// []byte
// string
// time.Time
rows, e := stmt.Query(([]driver.Value{int64(4), int64(5)}))//这个rows是driver.rows,不是sql.rows,故不能用下面的PirntQueryRows()方法,我们在下面新建了一个新的方法
// NumInput返回占位符参数的数量。
//如果NumInput返回> = 0,则sql程序包将在调用语句的Exec或Query方法之前从调用方进行完整性检查参数计数,并将错误返回给调用方。
//如果驱动程序不知道其占位符数量,则NumInput也可能返回-1。 在这种情况下,sql程序包将不会检查Exec或Query参数计数。
fmt.Println("stmt中的占位符的数量为:",stmt.NumInput())
//除了上面几个方法之外,还有stmt.Exec(),不再累叙
defer stmt.Close()
check_err_sql(e)
if e !=nil{
panic(e)
}
e = PirntQueryDriverRows(rows)
if e !=nil{
panic(e)
}
return nil
} )
check_err_sql(err)
//输出:
// stmt中的占位符的数量为: 2
// [id name age]
// []driver.Value{4, []uint8{0x70, 0x79, 0x74, 0x68, 0x6f, 0x6e}, 44}
// [4 [112 121 116 104 111 110] 44]
// [%!s(int64=4) python %!s(int64=44)]
// 4python44
// -------
// []driver.Value{5, []uint8{0x70, 0x79, 0x74, 0x68, 0x6f, 0x6e}, 44}
// [5 [112 121 116 104 111 110] 44]
// [%!s(int64=5) python %!s(int64=44)]
// 5python44
fmt.Println("-------探究是否关闭了conn---------")
rows, err = conn.QueryContext(background, "select * from person")
check_err_sql(err)
PirntQueryRows(rows)
//输出:
// {1 anko 20}
// {2 gogo 32}
// {3 cpp 23}
// {4 python 44}
// {5 python 44}
// {6 Zhongshan 44}
//从上可知,我们只是关闭了driverConn,但不是关闭conn
fmt.Println("---------------------验证连接是否有效------------------------")
//// Pinger is an optional interface that may be implemented by a Conn.
////
//// If a Conn does not implement Pinger, the sql package's DB.Ping and
//// DB.PingContext will check if there is at least one Conn available.
////
//// If Conn.Ping returns ErrBadConn, DB.Ping and DB.PingContext will remove
//// the Conn from pool.
//// Pinger是Conn可以实现的可选接口。
//// 如果Conn未实现Pinger,则sql包的DB.Ping和DB.PingContext将检查是否至少有一个Conn可用。
//// 如果Conn.Ping返回ErrBadConn,则DB.Ping和DB.PingContext将从池中删除Conn。
//type Pinger interface {
// Ping(ctx context.Context) error
//}
// PingContext verifies the connection to the database is still alive.
// PingContext验证与数据库的连接是否仍然有效。如果有效则直接建立连接
// 这个方法和*DB.ping()方法的底层一样都是调用了func (db *DB) PingContext(ctx context.Context) error{}方法
// 返回值是上面接口Pinger的error
err = conn.PingContext(background)
check_err_sql(err)
db_test, err := sql.Open("mysql", "root:mysql111@/godb")
check_err_sql(err)
//这个方法会尝试进行连接,如果连接出错,会抛出异常!因此在下面的PingContext会直接抛出异常,conn_test为nil,
// nil.PingContext()肯定会抛出异常的!
// 事实上Conn也是抛出了异常,不过我们在这里处理了
test:= func() {
conn_test, err := db_test.Conn(context.Background())
if err != nil{
fmt.Println("===1====",err)
}
fmt.Println("111111")
time.Sleep(1e9)
err = conn_test.PingContext(background)
if err != nil{
fmt.Println("===2====",err)
}
}
fmt.Println(test)
//test()
//输出:
// 0x5f1f70
// ===1==== Error 1045: Access denied for user 'root'@'localhost' (using password: YES)
// 111111
// panic: runtime error: invalid memory address or nil pointer dereference
// [signal 0xc0000005 code=0x0 addr=0x28 pc=0x4d7f7d]
//
// goroutine 1 [running]:
// database/sql.(*Conn).grabConn(0x0, 0x6b7580, 0xc000056010, 0xc00003c200, 0xc000028000, 0x8144a0, 0x8144a0)
// C:/Go/src/database/sql/sql.go:1798 +0x2d
// database/sql.(*Conn).PingContext(0x0, 0x6b7580, 0xc000056010, 0x1, 0x1)
// C:/Go/src/database/sql/sql.go:1807 +0x4a
// main.main()
// C:/Users/Administrator/Desktop/go_pro/src/io_pro/main3/compress_zlib.go:752 +0x2022
fmt.Println("-------------------事务------------------------")
//我们将上面的test()注释掉才可以进行下面的步骤
//// TxOptions holds the transaction options to be used in DB.BeginTx.
//// TxOptions保留要在DB.BeginTx中使用的事务选项。
//type TxOptions struct {
// // Isolation is the transaction isolation level.
// // If zero, the driver or database's default level is used.
// //Isolation隔离是事务隔离级别。
// //如果为零值,则使用驱动程序或数据库的默认级别。
// Isolation IsolationLevel
// ReadOnly bool //是否可读
//}
////IsolationLevel对象如下:
//// IsolationLevel is the transaction isolation level used in TxOptions.
//// IsolationLevel是TxOptions中使用的事务隔离级别。
//type IsolationLevel int
//
//// Various isolation levels that drivers may support in BeginTx.
//// If a driver does not support a given isolation level an error may be returned.
////
//// See https://en.wikipedia.org/wiki/Isolation_(database_systems)#Isolation_levels.
////驱动程序可以在BeginTx中支持的各种隔离级别。
////如果驱动程序不支持给定的隔离级别,则可能会返回错误。
////参见https://en.wikipedia.org/wiki/Isolation_(database_systems)#Isolation_levels。
//const (
// LevelDefault IsolationLevel = iota
// LevelReadUncommitted
// LevelReadCommitted
// LevelWriteCommitted
// LevelRepeatableRead
// LevelSnapshot
// LevelSerializable
// LevelLinearizable
//)
// BeginTx starts a transaction.
//
// The provided context is used until the transaction is committed or rolled back.
// If the context is canceled, the sql package will roll back
// the transaction. Tx.Commit will return an error if the context provided to
// BeginTx is canceled.
//
// The provided TxOptions is optional and may be nil if defaults should be used.
// If a non-default isolation level is used that the driver doesn't support,
// an error will be returned.
// BeginTx开始一个事务。
// 提供的上下文将一直使用,直到事务被提交或回滚为止。
// 如果取消上下文,则sql包将回滚该事务。 如果提供给BeginTx的上下文被取消,Tx.Commit将返回错误。
// 提供的TxOptions是可选的,如果使用默认值,则提供的TxOptions可能为nil。
// 如果使用了驱动程序不支持的非默认隔离级别,则将返回错误。
//如果是不想&sql.TxOptions给全字段值,那么就应该使用关键字的形式赋值
tx, err := conn.BeginTx(background, &sql.TxOptions {Isolation:sql.LevelDefault})
check_err_sql(err)
//rows_tx, err := tx.Query(`select * from person where id=? or id=?`, 4, 5)
result_tx, err := tx.Exec(`update person set name = "anko",age=11 where id=? or id=?`, 4, 5)
check_err_sql(err)
if err !=nil{
//回滚将中止事务。
err := tx.Rollback()
check_err_sql(err)
}
//还有以下的方法不再演示!同理的!最后的几个方法将会在下面讲解
//tx.QueryRowContext()
//tx.QueryRow()
//tx.QueryContext()
//tx.ExecContext()
//tx.PrepareContext()
//tx.Prepare()
//tx.Stmt()
//tx.StmtContext()
defer rows.Close()
check_err_sql(err)
//PirntQueryRows(rows_tx)//语句1的代码
//下面2行是语句2 的代码
fmt.Println(result_tx.LastInsertId())
fmt.Println(result_tx.RowsAffected())
// Commit commits the transaction.
// Commit提交事务。
//必须在PirntQueryRows()的下面
err = tx.Commit()
check_err_sql(err)
//语句1输出:
// {4 python 44}
// {5 python 44}
//语句2输出:
// 0 <nil>
// 2 <nil>
fmt.Println("---------------PingContext----------------------")
////故意写错账号
//db1, err := sql.Open("mysql", "root111:mysql@/godb")
//if err != nil {
// panic(err.Error())
//}
//
//// PingContext验证与数据库的连接是否仍然存在,并在必要时建立连接。
//// 这其实是db1.Ping()的底层实现
//err= db1.PingContext(context.Background())
//check_err_sql(err)
////输出:
////Error 1045: Access denied for user 'root111'@'localhost' (using password: YES)
fmt.Println("---------------PingContext----------------------")
db2, err := sql.Open("mysql", "root:mysql@/godb")
if err != nil {
panic(err.Error())
}
// BeginTx starts a transaction.
//
// The provided context is used until the transaction is committed or rolled back.
// If the context is canceled, the sql package will roll back
// the transaction. Tx.Commit will return an error if the context provided to
// BeginTx is canceled.
//
// The provided TxOptions is optional and may be nil if defaults should be used.
// If a non-default isolation level is used that the driver doesn't support,
// an error will be returned.
// BeginTx开始事务。
//提供的上下文将一直使用,直到事务被提交或回滚为止。
//如果取消上下文,则sql包将回滚该事务。 如果提供给BeginTx的上下文被取消,Tx.Commit将返回错误。
//提供的TxOptions是可选的,如果应使用默认值,则可以为nil。
//如果使用了驱动程序不支持的非默认隔离级别,则将返回错误。
tx,err= db2.BeginTx(context.Background(),&sql.TxOptions{Isolation: sql.LevelDefault})
check_err_sql(err)
//故意用不存在的键age111,好让事务回滚
//如果我给定错误或者不存在的id的话,程序不会回滚事务,而是无任何响应
//result, err = tx.ExecContext(context.Background(), `update person set name = "anko",age=22 where id=? or id=?`, 77, 88)
result, err = tx.ExecContext(context.Background(), `update person set name = "anko",age111=22 where id=? or id=?`, 4, 5)
check_err_sql(err)
if err != nil {
fmt.Println("准备回滚事务。。。")
err := tx.Rollback()
if err !=nil{
fmt.Println("(===1===)",err)
}
}else {
fmt.Println(result.LastInsertId())
fmt.Println(result.RowsAffected())
err = tx.Commit()
if err !=nil{
fmt.Println("(===2===)",err)
}
}
//输出如下:
//Error 1054: Unknown column 'age111' in 'field list'
//准备回滚事务。。。
//上面的这种写法才是最合适的!下面的写法仅仅是展示一些api的原理
//必须重新开启事务,上一个事务对象不允许被重用,因为他已经被取消了
tx,err= db2.BeginTx(context.Background(),&sql.TxOptions{Isolation: sql.LevelDefault})
check_err_sql(err)
// Prepare creates a prepared statement for use within a transaction.
//
// The returned statement operates within the transaction and can no longer
// be used once the transaction has been committed or rolled back.
//
// To use an existing prepared statement on this transaction, see Tx.Stmt.
// Prepare创建一个准备好的语句,供在事务中使用。
// 返回的语句在事务内运行,并且一旦事务已提交或回滚就不能再使用。
// 要在此事务上使用现有的准备好的语句,请参见Tx.Stmt。
//底层会先提交过去这个预填充的语句过去给数据库,如果没有当前的字段的话,tx.Prepare会捕捉到错误并且抛出同时stmt=nil。
//跟上一个api不同,上一个不需要准备语句,而是一次性发送完整的语句过去给数据库了!而不是分次发送语句过去给数据库!
stmt, err = tx.Prepare(`update person set name = "anko55",age=95 where id=? or id=?`)
if err !=nil{
fmt.Println("(===1===)",err)
//如果出错的话,我们应该在这里 进行结束,不再执行下面的语句,否则下面的语句会抛出异常!
panic(err)
}
// Exec用给定的参数执行一个准备好的语句,并返回一个总结语句效果的结果。
result, err = stmt.Exec(4, 5)//如果这里提交的id是不存在的话则不会抛出任何的错误,只是没反应!
if err !=nil{
fmt.Println("(===2===)",err)
}
if err != nil{
//如果出错直接回滚
fmt.Println("准备回滚事务。。。。")
err111 := tx.Rollback()
check_err_sql(err111)
}else {
fmt.Println(result.LastInsertId())
fmt.Println(result.RowsAffected())
}
//我们看下一个事务一个stmt语句能不能提交2次的填充词汇 或者 一个事务能否存在多条stmt语句,很明显是可以的!
stmt, err = tx.Prepare(`update person set name = "anko55",age=81 where id=? or id=?`)
result, err = stmt.Exec(2, 3)//如果这里提交的id是不存在的话则不会抛出任何的错误,只是没反应!
if err !=nil{
fmt.Println("(===2*2===)",err)
}
if err != nil{
//如果出错直接回滚
fmt.Println("准备回滚事务。。。。")
err111 := tx.Rollback()