Pembuatan kode di Go dengan contoh membuat klien untuk database

Pada artikel ini, saya ingin mempertimbangkan masalah pembuatan kode di Golang. Saya perhatikan bahwa sering dalam komentar pada artikel di Go menyebutkan pembuatan kode dan refleksi, yang menyebabkan perdebatan sengit. Pada saat yang sama, ada beberapa artikel tentang pembuatan kode di hub, meskipun cukup banyak digunakan dalam proyek di Go. Dalam artikel ini saya akan mencoba memberi tahu Anda apa yang dimaksud dengan pembuatan kode, untuk menggambarkan ruang lingkup aplikasi dengan contoh kode. Juga, saya tidak akan mengabaikan refleksi.

Saat pembuatan kode digunakan


Di Habré sudah ada artikel bagus tentang topik di sini dan di sini , saya tidak akan mengulanginya.

Pembuatan kode harus digunakan dalam kasus:

  • Meningkatkan kecepatan kode, yaitu untuk menggantikan refleksi;
  • Mengurangi rutinitas seorang programmer (dan kesalahan yang terkait dengannya);
  • Implementasi pembungkus sesuai aturan yang diberikan.

Dari contoh, kita dapat mempertimbangkan pustaka Stringer, yang termasuk dalam pasokan bahasa standar dan memungkinkan Anda untuk secara otomatis menghasilkan metode String () untuk set konstanta numerik. Dengan menggunakannya, Anda dapat mengimplementasikan output dari nama variabel. Contoh perpustakaan dijelaskan secara rinci dalam artikel di atas. Contoh yang paling menarik adalah derivasi nama warna dari palet. Aplikasi pembuatan kode di sana menghindari mengubah kode di beberapa tempat ketika mengubah palet.

Dari contoh yang lebih praktis, kita dapat menyebutkan perpustakaan easyjson dari Mail.ru. Perpustakaan ini memungkinkan Anda untuk mempercepat pelaksanaan JSON masrshall / unmarshall dari / ke struktur. Implementasi mereka pada tolok ukur melewati semua alternatif. Untuk menggunakan perpustakaan, Anda perlu memanggil easyjson, itu akan menghasilkan kode untuk semua struktur yang ditemukan dalam file yang ditransfer, atau hanya untuk mereka yang berkomentar di // easyjson: json. Ambil struktur pengguna sebagai contoh:

type User struct{
    ID int
    Login string
    Email string
    Level int
}

Untuk file yang ada di dalamnya, jalankan pembuatan kode:

easyjson -all main.go

Sebagai hasilnya, kami mendapatkan metode untuk Pengguna:

  • MarshalEasyJSON (w * jwriter.Writer) - untuk mengubah struktur menjadi array byte JSON;
  • UnmarshalEasyJSON (l * jlexer.Lexer) - untuk mengkonversi dari array byte menjadi sebuah struktur.

Fungsi kesalahan MarshalJSON () ([] byte, error) dan UnmarshalJSON (data [] byte) diperlukan untuk kompatibilitas dengan antarmuka json standar.

Kode Easyjson

func TestEasyJSON() {
	testJSON := `{"ID":123, "Login":"TestUser", "Email":"user@gmail.com", "Level":12}`
	JSONb := []byte(testJSON)
	fmt.Println(testJSON)
	recvUser := &User{}
	recvUser.UnmarshalJSON(JSONb)
	fmt.Println(recvUser)
	recvUser.Level += 1
	outJSON, _ := recvUser.MarshalJSON()
	fmt.Println(string(outJSON))
}


Dalam fungsi ini, pertama-tama kita mengubah JSON menjadi sebuah struktur, menambahkan satu level dan mencetak JSON yang dihasilkan. Pembuatan kode oleh easyjson berarti menyingkirkan refleksi runtime dan meningkatkan kinerja kode.

Pembuatan kode secara aktif digunakan untuk membuat layanan mikro yang berkomunikasi melalui gRPC. Ini menggunakan format protobuf untuk menggambarkan metode layanan - menggunakan bahasa perantara EDL. Setelah deskripsi layanan, kompiler protoc diluncurkan, yang menghasilkan kode untuk bahasa pemrograman yang diinginkan. Dalam kode yang dihasilkan, kami mendapatkan antarmuka yang perlu diimplementasikan di server dan metode yang digunakan pada klien untuk mengatur komunikasi. Ternyata cukup nyaman, kita dapat menggambarkan layanan kami dalam satu format dan menghasilkan kode untuk bahasa pemrograman di mana masing-masing elemen interaksi akan dijelaskan.

Juga, pembuatan kode dapat digunakan dalam pengembangan kerangka kerja. Misalnya, untuk mengimplementasikan kode yang tidak perlu ditulis oleh pengembang aplikasi, tetapi perlu untuk operasi yang benar. Misalnya, untuk membuat validator bidang formulir, generasi otomatis Middleware, generasi klien yang dinamis ke DBMS.

Implementasi Go Code Generator


Mari kita teliti dalam praktik bagaimana mekanisme pembuatan kode di Go bekerja. Pertama-tama, perlu menyebutkan AST - Abstract Syntax Tree atau Abstract Syntax Tree. Untuk detailnya, Anda bisa pergi ke Wikipedia . Untuk keperluan kami, perlu dipahami bahwa keseluruhan program dibangun dalam bentuk grafik, di mana simpul dipetakan (ditandai) dengan operator bahasa pemrograman, dan daun dengan operan yang sesuai.

Jadi, untuk permulaan, kita membutuhkan paket:

go / ast
go / parser /
go / token /

Parsing file dengan kode dan kompilasi pohon dilakukan oleh perintah berikut


fset := token.NewFileSet()
node, err := parser.ParseFile(fset, os.Args[1], nil, parser.ParseComments)

Kami menunjukkan bahwa nama file harus diambil dari argumen pertama dari baris perintah, kami juga meminta agar komentar ditambahkan ke struktur pohon.

Secara umum, untuk mengontrol pembuatan kode, pengguna (pengembang kode atas dasar mana kode lain dihasilkan) dapat menggunakan komentar atau tag (seperti yang kita tulis `json:" "` di dekat bidang struktur).

Sebagai contoh, kita akan menulis generator kode untuk bekerja dengan database. Pembuat kode akan melihat file yang ditransfer ke dalamnya, mencari struktur yang memiliki komentar terkait dan membuat pembungkus atas struktur (metode CRUD) untuk interaksi basis data. Kami akan menggunakan parameter:

  • komentar dbe: {"table": "users"}, di mana Anda dapat mendefinisikan tabel di mana catatan struktur akan;
  • Tag dbe untuk bidang struktur, di mana Anda dapat menentukan nama kolom di mana untuk meletakkan nilai bidang dan atribut untuk database: primary_key dan not_null. Mereka akan digunakan saat membuat tabel. Dan untuk nama bidang, Anda dapat menggunakan "-" agar tidak membuat kolom untuk itu.

Saya akan membuat reservasi terlebih dahulu bahwa proyek ini belum bertempur, itu tidak akan berisi bagian dari pemeriksaan dan perlindungan yang diperlukan. Jika ada minat, saya akan melanjutkan pengembangannya.

Jadi, kami telah memutuskan tugas dan parameter untuk mengendalikan pembuatan kode, kami dapat mulai menulis kode.

Tautan ke semua kode ada di akhir artikel.

Kami mulai melewati pohon yang dihasilkan dan akan mengurai setiap elemen dari tingkat pertama. Go memiliki tipe yang telah ditentukan untuk parsing: BadDecl, GenDecl, dan FuncDecl.

Ketikkan Deskripsi
// A BadDecl node is a placeholder for declarations containing
// syntax errors for which no correct declaration nodes can be
// created.
//
BadDecl struct {
    From, To token.Pos // position range of bad declaration
}
// A GenDecl node (generic declaration node) represents an import,
// constant, type or variable declaration. A valid Lparen position
// (Lparen.IsValid()) indicates a parenthesized declaration.
//
// Relationship between Tok value and Specs element type:
//
// token.IMPORT *ImportSpec
// token.CONST *ValueSpec
// token.TYPE *TypeSpec
// token.VAR *ValueSpec
//
GenDecl struct {
    Doc *CommentGroup // associated documentation; or nil
    TokPos token.Pos // position of Tok
    Tok token.Token // IMPORT, CONST, TYPE, VAR
    Lparen token.Pos // position of '(', if any
    Specs []Spec
    Rparen token.Pos // position of ')', if any
}
// A FuncDecl node represents a function declaration.
FuncDecl struct {
    Doc *CommentGroup // associated documentation; or nil
    Recv *FieldList // receiver (methods); or nil (functions)
    Name *Ident // function/method name
    Type *FuncType // function signature: parameters, results, and position of "func" keyword
    Body *BlockStmt // function body; or nil for external (non-Go) function
}


Kami tertarik pada struktur, jadi kami menggunakan GenDecl. Pada tahap ini, FuncDecl mungkin berguna, di mana definisi fungsi berada dan Anda membungkusnya, tetapi sekarang kita tidak membutuhkannya. Selanjutnya, kita melihat array Specs di setiap node, dan mencari bahwa kita sedang bekerja dengan bidang definisi tipe (* ast.TypeSpec) dan ini adalah struktur (* ast.StructType). Setelah kami menentukan bahwa kami memiliki struktur, kami memeriksa bahwa ia memiliki komentar // dbe. Kode traversal pohon lengkap dan definisi untuk struktur mana yang bekerja dengannya adalah di bawah ini.

Pohon traversal dan mendapatkan struktur

for _, f := range node.Decls {
	genD, ok := f.(*ast.GenDecl)
	if !ok {
		fmt.Printf("SKIP %T is not *ast.GenDecl\n", f)
		continue
	}
	targetStruct := &StructInfo{}
	var thisIsStruct bool
	for _, spec := range genD.Specs {
		currType, ok := spec.(*ast.TypeSpec)
		if !ok {
			fmt.Printf("SKIP %T is not ast.TypeSpec\n", spec)
			continue
		}

		currStruct, ok := currType.Type.(*ast.StructType)
		if !ok {
			fmt.Printf("SKIP %T is not ast.StructType\n", currStruct)
			continue
		}
		targetStruct.Name = currType.Name.Name
		thisIsStruct = true
	}
	//Getting comments
	var needCodegen bool
	var dbeParams string
	if thisIsStruct {
		for _, comment := range genD.Doc.List {
			needCodegen = needCodegen || strings.HasPrefix(comment.Text, "// dbe")
			if len(comment.Text) < 7 {
				dbeParams = ""
			} else {
				dbeParams = strings.Replace(comment.Text, "// dbe:", "", 1)
			}
		}
	}
	if needCodegen {
		targetStruct.Target = genD
		genParams := &DbeParam{}
		if len(dbeParams) != 0 {
			err := json.Unmarshal([]byte(dbeParams), genParams)
			if err != nil {
				fmt.Printf("Error encoding DBE params for structure %s\n", targetStruct.Name)
				continue
			}
		} else {
			genParams.TableName = targetStruct.Name
		}

		targetStruct.GenParam = genParams
		generateMethods(targetStruct, out)
	}
}

, :

type DbeParam struct {
	TableName string `json:"table"`
}

type StructInfo struct {
	Name     string
	GenParam *DbeParam
	Target   *ast.GenDecl
}


Sekarang kita akan menyiapkan informasi tentang bidang struktur, sehingga, berdasarkan informasi yang diterima, kita akan menghasilkan fungsi pembuatan tabel (createTable) dan metode CRUD.

Kode untuk mendapatkan bidang dari struktur

func generateMethods(reqStruct *StructInfo, out *os.File) {
	for _, spec := range reqStruct.Target.Specs {
		fmt.Fprintln(out, "")
		currType, ok := spec.(*ast.TypeSpec)
		if !ok {
			continue
		}
		currStruct, ok := currType.Type.(*ast.StructType)
		if !ok {
			continue
		}

		fmt.Printf("\tgenerating createTable methods for %s\n", currType.Name.Name)

		curTable := &TableInfo{
			TableName: reqStruct.GenParam.TableName,
			Columns:   make([]*ColInfo, 0, len(currStruct.Fields.List)),
		}

		for _, field := range currStruct.Fields.List {
			if len(field.Names) == 0 {
				continue
			}
			tableCol := &ColInfo{FieldName: field.Names[0].Name}
			var fieldIsPrimKey bool
			var preventThisField bool
			if field.Tag != nil {
				tag := reflect.StructTag(field.Tag.Value[1 : len(field.Tag.Value)-1])
				tagVal := tag.Get("dbe")
				fmt.Println("dbe:", tagVal)
				tagParams := strings.Split(tagVal, ",")
			PARAMSLOOP:
				for _, param := range tagParams {
					switch param {
					case "primary_key":
						if curTable.PrimaryKey == nil {
							fieldIsPrimKey = true
							tableCol.NotNull = true
						} else {
							log.Panicf("Table %s cannot have more then 1 primary key!", currType.Name.Name)
						}
					case "not_null":
						tableCol.NotNull = true
					case "-":
						preventThisField = true
						break PARAMSLOOP
					default:
						tableCol.ColName = param
					}

				}
				if preventThisField {
					continue
				}
			}
			if tableCol.ColName == "" {
				tableCol.ColName = tableCol.FieldName
			}
			if fieldIsPrimKey {
				curTable.PrimaryKey = tableCol
			}
			//Determine field type
			var fieldType string
			switch field.Type.(type) {
			case *ast.Ident:
				fieldType = field.Type.(*ast.Ident).Name
			case *ast.SelectorExpr:
				fieldType = field.Type.(*ast.SelectorExpr).Sel.Name
			}
			//fieldType := field.Type.(*ast.Ident).Name
			fmt.Printf("%s- %s\n", tableCol.FieldName, fieldType)
			//Check for integers
			if strings.Contains(fieldType, "int") {
				tableCol.ColType = "integer"
			} else {
				//Check for other types
				switch fieldType {
				case "string":
					tableCol.ColType = "text"
				case "bool":
					tableCol.ColType = "boolean"
				case "Time":
					tableCol.ColType = "TIMESTAMP"
				default:
					log.Panicf("Field type %s not supported", fieldType)
				}
			}
			tableCol.FieldType = fieldType
			curTable.Columns = append(curTable.Columns, tableCol)
			curTable.StructName = currType.Name.Name

		}
		curTable.generateCreateTable(out)

		fmt.Printf("\tgenerating CRUD methods for %s\n", currType.Name.Name)
		curTable.generateCreate(out)
		curTable.generateQuery(out)
		curTable.generateUpdate(out)
		curTable.generateDelete(out)
	}
}


Kami menelusuri semua bidang struktur yang diinginkan dan mulai mengurai tag masing-masing bidang. Dengan menggunakan refleksi, kami mendapatkan tag yang kami minati (toh, mungkin ada tag lain di lapangan, misalnya, untuk json). Kami menganalisis konten tag dan menentukan apakah bidang tersebut adalah kunci utama (jika lebih dari satu kunci utama ditentukan, mengutuknya, dan menghentikan eksekusi), apakah ada persyaratan untuk bidang tersebut menjadi nol, apakah kami perlu bekerja dengan database untuk bidang ini dan menentukan nama kolom jika diganti dalam tag. Kita juga perlu menentukan jenis kolom tabel berdasarkan pada jenis bidang struktur. Ada satu set terbatas jenis bidang, kami akan menghasilkan hanya untuk jenis dasar, kami akan mengurangi semua baris ke jenis bidang TEXT, meskipun secara umum, Anda dapat menambahkan definisi dari jenis kolom ke tag sehingga Anda dapat mengkonfigurasi lebih halus. Di samping itu,tidak ada yang mau membuat tabel yang diinginkan dalam database terlebih dahulu, atau untuk memperbaiki yang dibuat secara otomatis.

Setelah menguraikan struktur, kita mulai metode untuk membuat kode untuk fungsi pembuatan tabel dan metode untuk membuat fungsi Buat, Permintaan, Perbarui, Hapus. Kami menyiapkan ekspresi SQL untuk setiap fungsi dan mengikat untuk dijalankan. Saya tidak repot dengan penanganan kesalahan, saya hanya memberikan kesalahan dari driver basis data. Untuk pembuatan kode, lebih mudah menggunakan templat dari pustaka teks / templat. Dengan bantuan mereka, Anda bisa mendapatkan kode yang jauh lebih didukung dan dapat diprediksi (kode tersebut langsung terlihat, tetapi tidak diolesi oleh kode generator).

Pembuatan tabel

func (tableD *TableInfo) generateCreateTable(out *os.File) error {
	fmt.Fprint(out, "func (in *"+tableD.StructName+") createTable(db *sql.DB) (error) {\n")
	var resSQLq = fmt.Sprintf("\tsqlQ := `CREATE TABLE %s (\n", tableD.TableName)
	for _, col := range tableD.Columns {
		colSQL := col.ColName + " " + col.ColType
		if col.NotNull {
			colSQL += " NOT NULL"
		}
		if col == tableD.PrimaryKey {
			colSQL += " AUTO_INCREMENT"
		}
		colSQL += ",\n"
		resSQLq += colSQL
	}
	if tableD.PrimaryKey != nil {
		resSQLq += fmt.Sprintf("PRIMARY KEY (%s)\n", tableD.PrimaryKey.ColName)
	}
	resSQLq += ")`\n"
	fmt.Fprint(out, resSQLq)
	fmt.Fprint(out, "\t_, err := db.Exec(sqlQ)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n")
	fmt.Fprint(out, "\t return nil\n}\n\n")
	return nil
}


Tambahkan Catatan


	fmt.Fprint(out, "func (in *"+tableD.StructName+") Create(db *sql.DB) (error) {\n")
	var columns, valuePlaces, valuesListParams string
	for _, col := range tableD.Columns {
		if col == tableD.PrimaryKey {
			continue
		}
		columns += "`" + col.ColName + "`,"
		valuePlaces += "?,"
		valuesListParams += "in." + col.FieldName + ","
	}
	columns = columns[:len(columns)-1]
	valuePlaces = valuePlaces[:len(valuePlaces)-1]
	valuesListParams = valuesListParams[:len(valuesListParams)-1]

	resSQLq := fmt.Sprintf("\tsqlQ := \"INSERT INTO %s (%s) VALUES (%s);\"\n",
		tableD.TableName,
		columns,
		valuePlaces)
	fmt.Fprintln(out, resSQLq)
	fmt.Fprintf(out, "result, err := db.Exec(sqlQ, %s)\n", valuesListParams)
	fmt.Fprintln(out, `if err != nil {
		return err
	}`)
	//Setting id if we have primary key
	if tableD.PrimaryKey != nil {
		fmt.Fprintf(out, `lastId, err := result.LastInsertId()
		if err != nil {
			return nil
		}`)
		fmt.Fprintf(out, "\nin.%s = %s(lastId)\n", tableD.PrimaryKey.FieldName, tableD.PrimaryKey.FieldType)
	}
	fmt.Fprintln(out, "return nil\n}\n\n")
	//in., _ := result.LastInsertId()`)
	return nil
}


Mengambil catatan dari sebuah tabel

func (tableD *TableInfo) generateQuery(out *os.File) error {
	fmt.Fprint(out, "func (in *"+tableD.StructName+") Query(db *sql.DB) ([]*"+tableD.StructName+", error) {\n")

	fmt.Fprintf(out, "\tsqlQ := \"SELECT * FROM %s;\"\n", tableD.TableName)
	fmt.Fprintf(out, "rows, err := db.Query(sqlQ)\n")
	fmt.Fprintf(out, "results := make([]*%s, 0)\n", tableD.StructName)
	fmt.Fprintf(out, `for rows.Next() {`)
	fmt.Fprintf(out, "\t tempR := &%s{}\n", tableD.StructName)
	var valuesListParams string
	for _, col := range tableD.Columns {
		valuesListParams += "&tempR." + col.FieldName + ","
	}
	valuesListParams = valuesListParams[:len(valuesListParams)-1]

	fmt.Fprintf(out, "\terr = rows.Scan(%s)\n", valuesListParams)
	fmt.Fprintf(out, `if err != nil {
		return nil, err
		}`)
	fmt.Fprintf(out, "\n\tresults = append(results, tempR)")
	fmt.Fprintf(out, `}
		return results, nil
	}`)
	fmt.Fprintln(out, "")
	fmt.Fprintln(out, "")
	return nil
}


Rekam pembaruan (berfungsi dengan kunci utama)

func (tableD *TableInfo) generateUpdate(out *os.File) error {
	fmt.Fprint(out, "func (in *"+tableD.StructName+") Update(db *sql.DB) (error) {\n")
	var updVals, valuesListParams string
	for _, col := range tableD.Columns {
		if col == tableD.PrimaryKey {
			continue
		}
		updVals += "`" + col.ColName + "`=?,"
		valuesListParams += "in." + col.FieldName + ","
	}
	updVals = updVals[:len(updVals)-1]
	valuesListParams += "in." + tableD.PrimaryKey.FieldName

	resSQLq := fmt.Sprintf("\tsqlQ := \"UPDATE %s SET %s WHERE %s = ?;\"\n",
		tableD.TableName,
		updVals,
		tableD.PrimaryKey.ColName)
	fmt.Fprintln(out, resSQLq)
	fmt.Fprintf(out, "_, err := db.Exec(sqlQ, %s)\n", valuesListParams)
	fmt.Fprintln(out, `if err != nil {
		return err
	}`)

	fmt.Fprintln(out, "return nil\n}\n\n")
	//in., _ := result.LastInsertId()`)
	return nil
}


Hapus catatan (berfungsi dengan kunci utama)

func (tableD *TableInfo) generateDelete(out *os.File) error {
	fmt.Fprint(out, "func (in *"+tableD.StructName+") Delete(db *sql.DB) (error) {\n")
	fmt.Fprintf(out, "sqlQ := \"DELETE FROM %s WHERE id = ?\"\n", tableD.TableName)

	fmt.Fprintf(out, "_, err := db.Exec(sqlQ, in.%s)\n", tableD.PrimaryKey.FieldName)

	fmt.Fprintln(out, `if err != nil {
		return err
	}
	return nil
}`)
	fmt.Fprintln(out)
	return nil
}


Awal dari generator kode yang dihasilkan dijalankan oleh proses biasa, kami meneruskan path ke file yang Anda inginkan untuk menghasilkan kode di flag -name. Sebagai hasilnya, kita mendapatkan file dengan akhiran _dbe, di mana kode yang dihasilkan terletak. Untuk pengujian, buat metode untuk struktur berikut:


// dbe:{"table": "users"}
type User struct {
	ID       int    `dbe:"id,primary_key"`
	Login    string `dbe:"login,not_null"`
	Email    string
	Level    uint8
	IsActive bool
	UError   error `dbe:"-"`
}

Kode yang dihasilkan

package main

import "database/sql"

func (in *User) createTable(db *sql.DB) error {
	sqlQ := `CREATE TABLE users (
	id integer NOT NULL AUTO_INCREMENT,
	login text NOT NULL,
	Email text,
	Level integer,
	IsActive boolean,
	PRIMARY KEY (id)
	)`
	_, err := db.Exec(sqlQ)
	if err != nil {
		return err
	}
	return nil
}

func (in *User) Create(db *sql.DB) error {
	sqlQ := "INSERT INTO users (`login`,`Email`,`Level`,`IsActive`) VALUES (?,?,?,?);"

	result, err := db.Exec(sqlQ, in.Login, in.Email, in.Level, in.IsActive)
	if err != nil {
		return err
	}
	lastId, err := result.LastInsertId()
	if err != nil {
		return nil
	}
	in.ID = int(lastId)
	return nil
}

func (in *User) Query(db *sql.DB) ([]*User, error) {
	sqlQ := "SELECT * FROM users;"
	rows, err := db.Query(sqlQ)
	results := make([]*User, 0)
	for rows.Next() {
		tempR := &User{}
		err = rows.Scan(&tempR.ID, &tempR.Login, &tempR.Email, &tempR.Level, &tempR.IsActive)
		if err != nil {
			return nil, err
		}
		results = append(results, tempR)
	}
	return results, nil
}

func (in *User) Update(db *sql.DB) error {
	sqlQ := "UPDATE users SET `login`=?,`Email`=?,`Level`=?,`IsActive`=? WHERE id = ?;"

	_, err := db.Exec(sqlQ, in.Login, in.Email, in.Level, in.IsActive, in.ID)
	if err != nil {
		return err
	}
	return nil
}

func (in *User) Delete(db *sql.DB) error {
	sqlQ := "DELETE FROM users WHERE id = ?"
	_, err := db.Exec(sqlQ, in.ID)
	if err != nil {
		return err
	}
	return nil
}


Untuk menguji operasi kode yang dihasilkan, membuat objek dengan data acak, membuat tabel untuk itu (jika tabel ada dalam database, kesalahan akan dikembalikan). Setelah kami menempatkan objek ini di tabel, baca semua bidang dari tabel, perbarui nilai level dan hapus objek.

Panggil metode yang dihasilkan

var err error
db, err := sql.Open("mysql", DSN)
if err != nil {
	fmt.Println("Unable to connect to DB", err)
	return
}
err = db.Ping()
if err != nil {
	fmt.Println("Unable to ping BD")
	return
}
newUser := &User{
	Login:    "newUser",
	Email:    "new@test.com",
	Level:    0,
	IsActive: false,
	UError:   nil,
}

err = newUser.createTable(db)
if err != nil {
	fmt.Println("Error creating table.", err)

}
err = newUser.Create(db)
if err != nil {
	fmt.Println("Error creating user.", err)
	return
}

nU := &User{}
dbUsers, err := nU.Query(db)
if err != nil {
	fmt.Println("Error selecting users.", err)
	return
}
fmt.Printf("From table users selected %d fields", len(dbUsers))
var DBUser *User
for _, user := range dbUsers {
	fmt.Println(user)
	DBUser = user
}
DBUser.Level = 2
err = DBUser.Update(db)
if err != nil {
	fmt.Println("Error updating users.", err)
	return
}
err = DBUser.Delete(db)
if err != nil {
	fmt.Println("Error deleting users.", err)
	return
}


Dalam implementasi saat ini, fungsionalitas klien ke database sangat terbatas:

  • hanya MySQL yang didukung;
  • Tidak semua jenis bidang didukung.
  • tidak ada pemfilteran dan batasan untuk SELECT.

Namun, memperbaiki bug sudah di luar cakupan parsing kode sumber Go dan menghasilkan kode baru berdasarkan itu.

Menggunakan generator kode dalam skenario seperti itu akan memungkinkan Anda untuk mengubah bidang dan jenis struktur yang digunakan dalam aplikasi hanya di satu tempat, tidak perlu ingat untuk membuat perubahan pada kode untuk berinteraksi dengan database, Anda hanya perlu menjalankan generator kode setiap kali. Tugas ini dapat diselesaikan dengan bantuan refleksi, tetapi ini akan mempengaruhi kinerja.

Generator kode sumber dan contoh kode yang dihasilkan diposting di Github .

All Articles