- 投稿日:2020-09-08T22:58:50+09:00
GORMでの主要メソッドによるゼロ値の取り扱いについて
前提
gorm v2.0 で確認しています。
"gorm.io/gorm"
User 構造体は以下のものを使用しています。
type User struct { ID int `gorm:"column:id;primary_key:yes"` Email string `gorm:"column:email"` Age int `gorm:"column:age"` IsAdmin bool `gorm:"column:is_admin"` CreatedAt time.Time `gorm:"column:created_at"` UpdatedAt time.Time `gorm:"column:updated_at"` }Where
構造体を利用した書き方ですとフィールドがポインタ型でない場合ゼロ値("", 0, false)は検索できないため、事故を防ぐならベタで書くのが一番安全かと思います。
ただ、DB の構造体のフィールドをすべてポインタ型で宣言するようなルールになっている場合は構造体を利用した書き方で統一しても良いかと思います。
db.Where(User{Email: "example", Age: 10, IsAdmin: true}).Find(&user) // => SELECT * FROM `users` WHERE `users`.`email` = "example" AND `users`.`age` = 10 AND `users`.`is_admin` = true; db.Where(User{Email: "", Age: 0, IsAdmin: false}).Find(&user) // => SELECT * FROM `users`; db.Debug().Where("email = ?", "").Where("age = ?", 0).Where("is_admin", false).Find(&user) // => SELECT * FROM `users` WHERE email = "" AND age = 0 AND `is_admin` = false;Update
v1 では Where 句と同様に更新するフィールドがポインタ型でない場合はゼロ値の更新はできませんでした。
db.Where("id = ?", 1).Updates(User{Email: "", Age: 0, IsAdmin: false}) // => UPDATE `users` SET `updated_at`="2020-09-08 17:26:26.386" WHERE id = 1; db.Where("id = ?", 1).Updates(User{Email: "example", Age: 10, IsAdmin: true}) // => UPDATE `users` SET `email`="example",`age`=10,`is_admin`=true,`updated_at`="2020-09-08 17:25:07.501" WHERE id = 1;しかし、v2 で更新するカラムを明示的に選択した状態だとゼロ値でも更新することが出来るようになりました。
db.Select("is_admin").Where("id = ?", 1).Updates(User{Email: "", Age: 0, IsAdmin: false}) // => UPDATE `users` SET `is_admin`=false WHERE id = 1;Save
save はゼロ値に関わらず構造体に設定してる値を INSERT・UPDATE します
db.Debug().Save(&user) // => INSERT INTO `users` (`email`,`age`,`is_admin`,`created_at`,`updated_at`) VALUES ("",0,false,"2020-09-08 08:01:09","2020-09-08 13:34:23"); // => UPDATE `users` SET `email`="",`age`=0,`is_admin`=false,`created_at`="2020-09-08 08:01:09",`updated_at`="2020-09-08 22:34:22.664" WHERE `id` = 1;Delete
delete は特にゼロ値は関係ありませんので割愛。
オマケ
v2からUpdateとDeleteに条件文が必須になりましたので、全件更新・削除の事故がなくなりました。
primary_key:yes
つけ忘れて全件削除事故が起きた後にO/Rの仕様をしって驚愕したのはいい思い出です。
- 投稿日:2020-09-08T12:29:48+09:00
goでクロスコンパイル
環境変数に指定すると良いようだ。
対応OS
sample$ go tool dist list aix/ppc64 android/386 android/amd64 android/arm android/arm64 darwin/amd64 darwin/arm64 dragonfly/amd64 freebsd/386 freebsd/amd64 freebsd/arm freebsd/arm64 illumos/amd64 js/wasm linux/386 linux/amd64 linux/arm linux/arm64 linux/mips linux/mips64 linux/mips64le linux/mipsle linux/ppc64 linux/ppc64le linux/riscv64 linux/s390x netbsd/386 netbsd/amd64 netbsd/arm netbsd/arm64 openbsd/386 openbsd/amd64 openbsd/arm openbsd/arm64 plan9/386 plan9/amd64 plan9/arm solaris/amd64 windows/386 windows/amd64 windows/arm
クロスコンパイル
Windows用にコンパイル
Windows,64bit$ GOOS=windows GOARCH=amd64 go build -o hello.exe hello.go $ file hello.exe hello.exe: PE32+ executable (console) x86-64 (stripped to external PDB), for MS WindowsLinux用にコンパイル
Linux,x86_64$ GOOS=linux GOARCH=amd64 go build -o hello-linux,x64 hello.go $ file hello-linux,x64 hello-linux,x64: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, Go BuildID=3PtKe3LN9_4rxlzxXgHa/1uVXnXx5yplTTQQgjFlz/CgwczejsGQh2_tbwoQtP/i5_2ofMgEmSF8t-M_DMC, not strippedLinux,arm64$ GOOS=linux GOARCH=arm64 go build -o hello-linux,arm hello.go $ file hello-linux,arm hello-linux,arm: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), statically linked, Go BuildID=VovlihMkDllgU6NAxm-Z/kyuuVg3MXeB_YJcbORcv/QafLZDijlnE-kvNk7EjY/TtzedUs_SNx9-ovthhf3, not stripped
- 投稿日:2020-09-08T10:28:17+09:00
CentOS8にGolang/MariaDBをインストールし、DB接続してSQLを実行する
概要
Golang学習用備忘録です。
今回以下URLを参考にさせていただきました。Go 言語から MariaDB Server を使ってみる
GoでMySQLに接続するGolangインストール/動作確認
よろしければ、以下を参考にしてください。
CentOS8にGolangをインストールするMariaDBインストール/GoからMariadbへの接続準備
ターミナル#Mariadbインストール dnf install -y mariadb mariadb-server #MariaDBを起動・自動起動設定 systemctl start mariadb systemctl enable mariadb #MariaDBのrootユーザーの初期パスワードを設定 /usr/bin/mysql_secure_installation #Set root password? [Y/n] y →「y」を選択 #New password: →入力 #Re-enter new password: →再入力 #Password updated successfully! #Mariadb バージョン確認 mysql --version #バージョン情報が表示されること #gitがインストールされていない場合には、下記コマンドにて #インストールを行う dnf -y install git #MariaDBからGo用Connectorが提供されていないため、 #GitHubで公開されているMySQL用ドライバを使用 go get github.com/go-sql-driver/mysql
- 接続用Goモジュール作成
check_mariadb_ver.gopackage main import( "database/sql" "fmt" _ "github.com/go-sql-driver/mysql" ) func main(){ //データベースに接続 db, err := sql.Open("mysql", "ユーザ名:パスワード@/データベース名") if err != nil { fmt.Println(err.Error()) } defer db.Close() //バージョン情報確認 var version string db.QueryRow("SELECT VERSION()").Scan(&version) //バージョン情報出力 fmt.Println("connected to:", version) }
- モジュール実行
ターミナル#実行 go run check_mariadb_ver.go #バージョン情報が出力されること connected to: 10.3.17-MariaDBまとめ
GoからMariadbに接続して、SQLを実行するところまでをみていきました。
今後は、Goを活用してのAPI作成等行い、
最終的には、簡単なアプリケーションを開発してみたいと思います。
- 投稿日:2020-09-08T08:08:31+09:00
AtCoder Library Practice Contest 参戦記 (Go)
AtCoder Library Practice Contest 参戦記 (Go)
ACL Contest は 1200 から rated なので rated で出場できるかどうしようと考えつつ、AtCoder Library Practice Contest を解いてみるテスト.
PRACTICE2A - Disjoint Set Union
ACL に Union Find 入ってないような、見落としてる??? あ、DSU = Disjoint Set Union = Union Find か.
package main import ( "bufio" "fmt" "os" "strconv" ) func find(parent []int, i int) int { if parent[i] < 0 { return i } parent[i] = find(parent, parent[i]) return parent[i] } func unite(parent []int, i, j int) { i = find(parent, i) j = find(parent, j) if i == j { return } parent[j] += parent[i] parent[i] = j } func main() { defer flush() N := readInt() Q := readInt() parent := make([]int, N) for i := 0; i < N; i++ { parent[i] = -1 } for i := 0; i < Q; i++ { t := readInt() u := readInt() v := readInt() if t == 0 { unite(parent, u, v) } else if t == 1 { if find(parent, u) == find(parent, v) { println(1) } else { println(0) } } } } const ( ioBufferSize = 1 * 1024 * 1024 // 1 MB ) var stdinScanner = func() *bufio.Scanner { result := bufio.NewScanner(os.Stdin) result.Buffer(make([]byte, ioBufferSize), ioBufferSize) result.Split(bufio.ScanWords) return result }() func readString() string { stdinScanner.Scan() return stdinScanner.Text() } func readInt() int { result, err := strconv.Atoi(readString()) if err != nil { panic(err) } return result } var stdoutWriter = bufio.NewWriter(os.Stdout) func flush() { stdoutWriter.Flush() } func println(args ...interface{}) (int, error) { return fmt.Fprintln(stdoutWriter, args...) }PRACTICE2B - Fenwick Tree
3度目の登板.
package main import ( "bufio" "fmt" "os" "strconv" ) // BIT stands for binary indexed tree. type BIT []int func newBIT(n int) BIT { return make([]int, n) } func (bit BIT) add(i, v int) { for i++; i <= len(bit); i += i & -i { bit[i-1] += v } } func (bit BIT) sum(i int) int { result := 0 for i++; i > 0; i -= i & -i { result += bit[i-1] } return result } func (bit BIT) query(start int, stop int) int { return bit.sum(stop-1) - bit.sum(start-1) } func main() { defer flush() N := readInt() Q := readInt() a := make([]int, N) for i := 0; i < N; i++ { a[i] = readInt() } bit := newBIT(N) for i := 0; i < N; i++ { bit.add(i, a[i]) } for i := 0; i < Q; i++ { t := readInt() if t == 0 { p := readInt() x := readInt() bit.add(p, x) } else if t == 1 { l := readInt() r := readInt() println(bit.query(l, r)) } } } const ( ioBufferSize = 1 * 1024 * 1024 // 1 MB ) var stdinScanner = func() *bufio.Scanner { result := bufio.NewScanner(os.Stdin) result.Buffer(make([]byte, ioBufferSize), ioBufferSize) result.Split(bufio.ScanWords) return result }() func readString() string { stdinScanner.Scan() return stdinScanner.Text() } func readInt() int { result, err := strconv.Atoi(readString()) if err != nil { panic(err) } return result } var stdoutWriter = bufio.NewWriter(os.Stdout) func flush() { stdoutWriter.Flush() } func println(args ...interface{}) (int, error) { return fmt.Fprintln(stdoutWriter, args...) }PRACTICE2J - Segment Tree
min_left もメソッドにするべきかな.
package main import ( "bufio" "fmt" "os" "strconv" ) func max(x, y int) int { if x > y { return x } return y } type segmentTree struct { offset int data []int op func(x, y int) int e int } func newSegmentTree(n int, op func(x, y int) int, e int) segmentTree { var result segmentTree t := 1 for t < n { t *= 2 } result.offset = t - 1 result.data = make([]int, 2*t-1) for i := 0; i < len(result.data); i++ { result.data[i] = e } result.op = op result.e = e return result } func (st segmentTree) build(a []int) { for i, v := range a { st.data[st.offset+i] = v } for i := st.offset - 1; i > -1; i-- { st.data[i] = st.op(st.data[i*2+1], st.data[i*2+2]) } } func (st segmentTree) update(index, value int) { i := st.offset + index st.data[i] = value for i >= 1 { i = (i - 1) / 2 st.data[i] = st.op(st.data[i*2+1], st.data[i*2+2]) } } func (st segmentTree) query(start, stop int) int { result := st.e l := start + st.offset r := stop + st.offset for l < r { if l&1 == 0 { result = st.op(result, st.data[l]) } if r&1 == 0 { result = st.op(result, st.data[r-1]) } l = l / 2 r = (r - 1) / 2 } return result } func main() { defer flush() N := readInt() Q := readInt() A := make([]int, N) for i := 0; i < N; i++ { A[i] = readInt() } st := newSegmentTree(N, max, -1) st.build(A) for i := 0; i < Q; i++ { T := readInt() if T == 1 || T == 3 { X := readInt() V := readInt() if T == 1 { st.update(X-1, V) } else if T == 3 { ok := N + 1 ng := X - 1 for ok-ng > 1 { m := ng + (ok-ng)/2 if st.query(X-1, m) >= V { ok = m } else { ng = m } } println(ok) } } else if T == 2 { L := readInt() R := readInt() println(st.query(L-1, R)) } } } const ( ioBufferSize = 1 * 1024 * 1024 // 1 MB ) var stdinScanner = func() *bufio.Scanner { result := bufio.NewScanner(os.Stdin) result.Buffer(make([]byte, ioBufferSize), ioBufferSize) result.Split(bufio.ScanWords) return result }() func readString() string { stdinScanner.Scan() return stdinScanner.Text() } func readInt() int { result, err := strconv.Atoi(readString()) if err != nil { panic(err) } return result } var stdoutWriter = bufio.NewWriter(os.Stdout) func flush() { stdoutWriter.Flush() } func println(args ...interface{}) (int, error) { return fmt.Fprintln(stdoutWriter, args...) }
- 投稿日:2020-09-08T06:31:21+09:00
Go言語で kintone REST API から データを取得する
Go言語からkintone REST API を叩いて、kintoneアプリのフィールド情報を取得するサンプルです。
APIは下記参照
https://developer.cybozu.io/hc/ja/articles/204783170#anchor_getform_fields環境
- go version go1.14.2 darwin/amd64
- Mac
設定ファイル
GoDotEnvライブラリを利用。
.envファイルにkintone接続情報を設定します。
https://github.com/joho/godotenvサンプルコード
package main import ( "bytes" "encoding/json" "fmt" "io" "io/ioutil" "log" "net/http" "os" "github.com/joho/godotenv" ) func main() { err := godotenv.Load() if err != nil { log.Fatal("Error loading .env file") } ba, _ := fetchFieldsJson(os.Getenv("APP"), os.Getenv("SUB_DOMAIN"), os.Getenv("API_TOKEN")) var out bytes.Buffer if err := json.Indent(&out, ba, "", " "); err != nil { log.Fatal("Error json indent", err) } out.WriteTo(os.Stdout) } func fetchFieldsJson(appid string, subdomain string, apitoken string) ([]byte, error) { client := &http.Client{} type Body struct { App string `json:"app"` } data := Body{appid} body, err := json.Marshal(data) if err != nil { log.Fatal("Error JSON Encode") } req, err := http.NewRequest("GET", "https://"+subdomain+".cybozu.com/k/v1/app/form/fields.json", bytes.NewReader(body)) if err != nil { log.Fatal("Error request") } req.Header.Add("X-Cybozu-API-Token", apitoken) req.Header.Add("Content-Type", "application/json") resp, err := client.Do(req) if err != nil { log.Fatal("Error client") } defer resp.Body.Close() fmt.Println(resp.Status) ba, err := ioutil.ReadAll(resp.Body) if err != nil { log.Fatal("Error read") } return ba, err }参考
- Go-lang https://golang.org/
- Package http https://golang.org/pkg/net/http/
- kintoneアプリのフィールド情報取得 https://developer.cybozu.io/hc/ja/articles/204783170#anchor_getform_fields
Go言語からkintoneのレコード取得の方法は、下記記事が参考になります。
kintone REST API について (GET編)