重慶網(wǎng)站產(chǎn)品推廣汕頭網(wǎng)站制作設(shè)計(jì)
前言
隔壁組的云計(jì)算零零后女同事,后文簡(jiǎn)稱 云女士 ,非說(shuō) Go 的 Gin 框架比 Springboot 更加的開箱即用,我心想在 Java 里面 Springboot 已經(jīng)打遍天下無(wú)敵手,這份底蘊(yùn)豈是 Gin 能比。
但是云女士突出一個(gè)執(zhí)拗,非我要 PK 一把, PK 內(nèi)容就是她使用 Gin,而我使用 Springboot 快速搭建一個(gè)簡(jiǎn)單的 Crud 工程,最后讓其他同事來(lái)評(píng)判哪個(gè)更開箱即用。我毫不猶豫就答應(yīng)了,作為搭建 Springboot 學(xué)習(xí)工程的資深 Crud 選手,咱這份底氣還是有的。
云女士選擇使用 Gin + Gorm 來(lái)搭建,而我原本想選擇 Springboot + MyBatis,后面轉(zhuǎn)念一想,這 MyBatis 要寫 XML 文件,指不定就因?yàn)檫@個(gè)被云女士嘲笑了,所以我把 MyBatis 替換為了 MyBatis-Plus,這就足夠的簡(jiǎn)潔了吧。
正文
準(zhǔn)備事項(xiàng)
既然是 Crud 工程,自然要準(zhǔn)備好操作的表,我和云女士通過(guò)如下語(yǔ)句在各自的數(shù)據(jù)庫(kù)中創(chuàng)建好了如下兩張表。
CREATE TABLE people (id INT(11) PRIMARY KEY AUTO_INCREMENT,p_name VARCHAR(255) NOT NULL,p_age INT(11) NOT NULL
)CREATE TABLE book (id INT(11) PRIMARY KEY AUTO_INCREMENT,b_name VARCHAR(255) NOT NULL,b_price FLOAT NOT NULL
)
Gin快速搭建Crud工程
云女士的工程結(jié)構(gòu)如下所示。
云女士的 go.mod 文件內(nèi)容如下所示。
module gobasego 1.17require (github.com/gin-gonic/gin v1.6.0github.com/jinzhu/gorm v1.9.16github.com/sirupsen/logrus v1.9.3github.com/spf13/cast v1.5.1
)
云女士定義了兩個(gè)結(jié)構(gòu)體作為模型( Model ),book.go 文件內(nèi)容如下所示。
package modelconst (BookTableName = "book"
)type Book struct {ID int64 `gorm:"column:id"`BookName string `gorm:"column:b_name"`BookPrice float64 `gorm:"column:b_price"`
}func (b *Book) TableName() string {return BookTableName
}
people.go 文件內(nèi)容如下所示。
package modelconst (PeopleTableName = "people"
)type People struct {ID int64 `gorm:"column:id"`PeopleName string `gorm:"column:p_name"`PeopleAge int64 `gorm:"column:p_age"`
}func (p *People) TableName() string {return PeopleTableName
}
云女士補(bǔ)充道,TableName()
方法是為模型指定對(duì)應(yīng)的表名。
云女士為 book 表和 people 表分別定義了 Dao 接口,dao.go 文件內(nèi)容如下所示。
package daoimport "gobase/model"type BookDao interface {AddBook(book *model.Book) errorUpdateBook(book *model.Book) errorDeleteBook(book *model.Book) errorListBookById(id uint) (*model.Book, error)
}type PeopleDao interface {AddPeople(book *model.People) errorUpdatePeople(book *model.People) errorDeletePeople(book *model.People) errorListPeopleById(id uint) (*model.People, error)
}
BookDao 接口對(duì)應(yīng)的實(shí)現(xiàn)在book_dao_impl.go
文件中,實(shí)現(xiàn)如下。
package daoimport ("github.com/jinzhu/gorm""gobase/model"
)type BookDaoImpl struct {DB *gorm.DB
}func (b *BookDaoImpl) AddBook(book *model.Book) error {if createResult := b.DB.Create(book); createResult.Error != nil {return createResult.Error}return nil
}func (b *BookDaoImpl) UpdateBook(book *model.Book) error {if saveResult := b.DB.Save(book); saveResult.Error != nil {return saveResult.Error}return nil
}func (b *BookDaoImpl) DeleteBook(book *model.Book) error {if deleteResult := b.DB.Delete(book); deleteResult.Error != nil {return deleteResult.Error}return nil
}func (b *BookDaoImpl) ListBookById(id uint) (*model.Book, error) {var book model.Bookif listResult := b.DB.Where("id = ?", id).First(&book); listResult.Error != nil {return nil, listResult.Error}return &book, nil
}
PeopleDao 接口對(duì)應(yīng)的實(shí)現(xiàn)在people_dao_impl.go
文件中,實(shí)現(xiàn)如下。
package daoimport ("github.com/jinzhu/gorm""gobase/model"
)type PeopleDaoImpl struct {DB *gorm.DB
}func (b *PeopleDaoImpl) AddPeople(people *model.People) error {if createResult := b.DB.Create(people); createResult.Error != nil {return createResult.Error}return nil
}func (b *PeopleDaoImpl) UpdatePeople(people *model.People) error {if saveResult := b.DB.Save(people); saveResult.Error != nil {return saveResult.Error}return nil
}func (b *PeopleDaoImpl) DeletePeople(people *model.People) error {if deleteResult := b.DB.Delete(people); deleteResult.Error != nil {return deleteResult.Error}return nil
}func (b *PeopleDaoImpl) ListPeopleById(id uint) (*model.People, error) {var people model.Peopleif listResult := b.DB.Where("id = ?", id).First(&people); listResult.Error != nil {return nil, listResult.Error}return &people, nil
}
要操作數(shù)據(jù)庫(kù),肯定需要數(shù)據(jù)庫(kù)連接,云女士將數(shù)據(jù)庫(kù)連接的管理實(shí)現(xiàn)在了mysql_connection_pool.go
文件中,內(nèi)容如下所示。
package mysqlimport ("fmt""github.com/jinzhu/gorm""gobase/dao""log""time"
)const (UserName = "root"PassWord = "root"Host = "192.168.101.8"Port = 3306Database = "gotest"MaxLifetime = 60 * time.SecondMaxIdletime = 30 * time.SecondMaxOpenconns = 6MaxIdleconns = 2Dialect = "mysql"
)type DataSouce struct {db *gorm.DB
}func NewDataSource() *DataSouce {var db *gorm.DBdsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8&parseTime=True&loc=Asia%%2FShanghai",UserName, PassWord, Host, Port, Database)db, err := gorm.Open(Dialect, dsn)if err != nil {log.Fatal(err.Error())}db.DB().SetConnMaxLifetime(MaxLifetime)db.DB().SetConnMaxIdleTime(MaxIdletime)db.DB().SetMaxOpenConns(MaxOpenconns)db.DB().SetMaxOpenConns(MaxIdleconns)return &DataSouce{db: db,}
}// BookDao 操作book表
func (d *DataSouce) BookDao() dao.BookDao {return &dao.BookDaoImpl{DB: d.db,}
}// PeopleDao 操作people表
func (d *DataSouce) PeopleDao() dao.PeopleDao {return &dao.PeopleDaoImpl{DB: d.db,}
}
云女士將路由寫在了webservice.go
文件中,內(nèi)容如下。
package adapterimport ("github.com/gin-gonic/gin""gobase/mysql"
)func Init() error {dataSouce := mysql.NewDataSource()bookController := NewBookController(dataSouce)propleController := NewPropleController(dataSouce)engine := gin.Default()routerGroupBook := engine.Group("/book")routerGroupBook.POST("/add", bookController.AddBook)routerGroupBook.POST("/update", bookController.UpdateBook)routerGroupBook.POST("/delete", bookController.DeleteBook)routerGroupBook.POST("/list", bookController.ListBookById)routerGroupPeople := engine.Group("/people")routerGroupPeople.POST("/add", propleController.AddPeople)routerGroupPeople.POST("/update", propleController.UpdatePeople)routerGroupPeople.POST("/delete", propleController.DeletePeople)routerGroupPeople.POST("/list", propleController.ListPeopleById)return engine.Run()
}
其實(shí)除了綁定路由,云女士還在Init()
函數(shù)中進(jìn)行了簡(jiǎn)單的服務(wù)注入,也就是創(chuàng)建數(shù)據(jù)庫(kù)連接池,然后將數(shù)據(jù)庫(kù)連接池給到對(duì)應(yīng)的 web 服務(wù)。
云女士將操作 book 表對(duì)應(yīng)的 web 服務(wù)寫在了book_controller.go
文件中,其實(shí)現(xiàn)如下所示。
package adapterimport ("github.com/gin-gonic/gin""github.com/sirupsen/logrus""github.com/spf13/cast""gobase/model""gobase/mysql""net/http"
)type BookController struct {dataSource *mysql.DataSouce
}func NewBookController(dataSource *mysql.DataSouce) BookController {return BookController{dataSource: dataSource,}
}func (b *BookController) AddBook(ctx *gin.Context) {var book model.Bookif err := ctx.ShouldBind(&book); err != nil {logrus.Error("讀取Book信息失敗")ctx.JSON(http.StatusInternalServerError, gin.H{"message": "failed",})return}bookDao := b.dataSource.BookDao()err := bookDao.AddBook(&book)if err != nil {logrus.Error("添加Book失敗", err)ctx.JSON(http.StatusInternalServerError, gin.H{"message": "failed",})return}ctx.JSON(http.StatusOK, gin.H{"message": "success",})
}func (b *BookController) UpdateBook(ctx *gin.Context) {var book model.Bookif err := ctx.ShouldBind(&book); err != nil {logrus.Error("讀取Book信息失敗")ctx.JSON(http.StatusInternalServerError, gin.H{"message": "failed",})return}bookDao := b.dataSource.BookDao()err := bookDao.UpdateBook(&book)if err != nil {logrus.Error("更新Book失敗", err)ctx.JSON(http.StatusInternalServerError, gin.H{"message": "failed",})return}ctx.JSON(http.StatusOK, gin.H{"message": "success",})
}func (b *BookController) DeleteBook(ctx *gin.Context) {var book model.Bookif err := ctx.ShouldBind(&book); err != nil {logrus.Error("讀取Book信息失敗")ctx.JSON(http.StatusInternalServerError, gin.H{"message": "failed",})return}bookDao := b.dataSource.BookDao()err := bookDao.DeleteBook(&book)if err != nil {logrus.Error("刪除Book失敗", err)ctx.JSON(http.StatusInternalServerError, gin.H{"message": "failed",})return}ctx.JSON(http.StatusOK, gin.H{"message": "success",})
}func (b *BookController) ListBookById(ctx *gin.Context) {id := cast.ToUint(ctx.Query("id"))bookDao := b.dataSource.BookDao()book, err := bookDao.ListBookById(id)if err != nil {logrus.Error("查詢Book失敗", err)ctx.JSON(http.StatusInternalServerError, gin.H{"message": "failed",})return}ctx.JSON(http.StatusOK, book)
}
云女士將操作 people 表對(duì)應(yīng)的 web 服務(wù)寫在了people_controller.go
文件中,其實(shí)現(xiàn)如下所示。
package adapterimport ("github.com/gin-gonic/gin""github.com/sirupsen/logrus""github.com/spf13/cast""gobase/model""gobase/mysql""net/http"
)type PeopleController struct {dataSource *mysql.DataSouce
}func NewPropleController(dataSource *mysql.DataSouce) PeopleController {return PeopleController{dataSource: dataSource,}
}func (p *PeopleController) AddPeople(ctx *gin.Context) {var people model.Peopleif err := ctx.ShouldBind(&people); err != nil {logrus.Error("讀取People信息失敗")ctx.JSON(http.StatusInternalServerError, gin.H{"message": "failed",})return}peopleDao := p.dataSource.PeopleDao()err := peopleDao.AddPeople(&people)if err != nil {logrus.Error("添加People失敗", err)ctx.JSON(http.StatusInternalServerError, gin.H{"message": "failed",})return}ctx.JSON(http.StatusOK, gin.H{"message": "success",})
}func (p *PeopleController) UpdatePeople(ctx *gin.Context) {var people model.Peopleif err := ctx.ShouldBind(&people); err != nil {logrus.Error("讀取People信息失敗")ctx.JSON(http.StatusInternalServerError, gin.H{"message": "failed",})return}peopleDao := p.dataSource.PeopleDao()err := peopleDao.UpdatePeople(&people)if err != nil {logrus.Error("更新People失敗", err)ctx.JSON(http.StatusInternalServerError, gin.H{"message": "failed",})return}ctx.JSON(http.StatusOK, gin.H{"message": "success",})
}func (p *PeopleController) DeletePeople(ctx *gin.Context) {var people model.Peopleif err := ctx.ShouldBind(&people); err != nil {logrus.Error("讀取People信息失敗")ctx.JSON(http.StatusInternalServerError, gin.H{"message": "failed",})return}peopleDao := p.dataSource.PeopleDao()err := peopleDao.DeletePeople(&people)if err != nil {logrus.Error("刪除People失敗", err)ctx.JSON(http.StatusInternalServerError, gin.H{"message": "failed",})return}ctx.JSON(http.StatusOK, gin.H{"message": "success",})
}func (p *PeopleController) ListPeopleById(ctx *gin.Context) {id := cast.ToUint(ctx.Query("id"))peopleDao := p.dataSource.PeopleDao()people, err := peopleDao.ListPeopleById(id)if err != nil {logrus.Error("查詢People失敗", err)ctx.JSON(http.StatusInternalServerError, gin.H{"message": "failed",})return}ctx.JSON(http.StatusOK, people)
}
最后,云女士簡(jiǎn)單的展示了一下對(duì) book 表和 prople 表的 Crud 操作。
book 表和 people 表的增刪改成功時(shí)返回內(nèi)容如下所示。
book 表和 people 表的查詢成功時(shí)返回內(nèi)容如下所示。
Spring boot 快速搭建Crud工程
Spring Boot 基礎(chǔ)就不介紹了
云女士基于 Gin 和 Gorm 搭建的 Crud 工程,我看完后內(nèi)心撲哧一笑:不過(guò)如此。
那現(xiàn)在該輪到我表演了。首先給出整個(gè)工程結(jié)構(gòu)圖如下所示。
POM 文件內(nèi)容如下所示。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.4.1</version></parent><groupId>com.lee.javabase</groupId><artifactId>javabase</artifactId><version>1.0-SNAPSHOT</version><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.1.0</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.16</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency></dependencies></project>
同樣,定義 book 表和 people 表對(duì)應(yīng)的實(shí)體類 Book 和 People,如下所示。
@Getter
@Setter
public class Book {@TableField("id")private int id;@TableField("b_name")private String bookName;@TableField("b_price")private float bookPrice;}@Getter
@Setter
public class People {@TableField("id")private int id;@TableField("p_name")private String peopleName;@TableField("p_age")private int peopleAge;}
然后定義定義接口,如下所示。
@Mapper
public interface BookMapper extends BaseMapper<Book> {
}@Mapper
public interface PeopleMapper extends BaseMapper<People> {
}
最后是對(duì)應(yīng)的 Controller 實(shí)現(xiàn), BookController 實(shí)現(xiàn)如下。
@Slf4j
@RestController
@RequestMapping("/book")
public class BookController {@Autowiredprivate BookMapper bookMapper;@PostMapping("/add")public ResponseEntity<String> addBook(@RequestBody Book book) {try {bookMapper.insert(book);return new ResponseEntity<>("添加圖書成功", HttpStatus.OK);} catch (Exception e) {log.error("添加圖書失敗", e);return new ResponseEntity<>("添加圖書失敗", HttpStatus.INTERNAL_SERVER_ERROR);}}@PostMapping("/update")public ResponseEntity<String> updateBook(@RequestBody Book book) {try {bookMapper.updateById(book);return new ResponseEntity<>("更新圖書成功", HttpStatus.OK);} catch (Exception e) {log.error("更新圖書失敗", e);return new ResponseEntity<>("更新圖書失敗", HttpStatus.INTERNAL_SERVER_ERROR);}}@PostMapping("/delete")public ResponseEntity<String> deleteBook(@RequestParam("id") int id) {try {bookMapper.deleteById(id);return new ResponseEntity<>("刪除圖書成功", HttpStatus.OK);} catch (Exception e) {log.error("刪除圖書失敗", e);return new ResponseEntity<>("刪除圖書失敗", HttpStatus.INTERNAL_SERVER_ERROR);}}@PostMapping("/list")public ResponseEntity<Book> listBook(@RequestParam("id") int id) {try {Book book = bookMapper.selectById(id);return new ResponseEntity<>(book, HttpStatus.OK);} catch (Exception e) {log.error("查詢圖書失敗", e);return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);}}
}
PeopleController 實(shí)現(xiàn)如下所示。
@Slf4j
@RestController
@RequestMapping("/people")
public class PeopleController {@Autowiredprivate PeopleMapper peopleMapper;@PostMapping("/add")public ResponseEntity<String> addPeople(@RequestBody People people) {try {peopleMapper.insert(people);return new ResponseEntity<>("添加人物成功", HttpStatus.OK);} catch (Exception e) {log.error("添加人物失敗", e);return new ResponseEntity<>("添加人物失敗", HttpStatus.INTERNAL_SERVER_ERROR);}}@PostMapping("/update")public ResponseEntity<String> updatePeople(@RequestBody People people) {try {peopleMapper.updateById(people);return new ResponseEntity<>("更新人物成功", HttpStatus.OK);} catch (Exception e) {log.error("更新人物失敗", e);return new ResponseEntity<>("更新人物失敗", HttpStatus.INTERNAL_SERVER_ERROR);}}@PostMapping("/delete")public ResponseEntity<String> deletePeople(@RequestParam("id") int id) {try {peopleMapper.deleteById(id);return new ResponseEntity<>("刪除人物成功", HttpStatus.OK);} catch (Exception e) {log.error("刪除人物失敗", e);return new ResponseEntity<>("刪除人物失敗", HttpStatus.INTERNAL_SERVER_ERROR);}}@PostMapping("/list")public ResponseEntity<People> listPeople(@RequestParam("id") int id) {try {People people = peopleMapper.selectById(id);return new ResponseEntity<>(people, HttpStatus.OK);} catch (Exception e) {log.error("查詢?nèi)宋锸?#34;, e);return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);}}
}
啟動(dòng)應(yīng)用程序, book 表的 Crud 操作結(jié)果如下所示。
prople 表的 Crud 操作結(jié)果如下所示。
總結(jié)
我宣布,Springboot 就是快速搭建 Crud 工程的神
。
其實(shí),在基于 Gin 和 Gorm 搭建 Crud 工程時(shí),云女士還是寫得復(fù)雜了一點(diǎn),但是我有幸看過(guò)她們?cè)破脚_(tái)的項(xiàng)目的代碼,云女士寫得也沒(méi)毛病,雖然是個(gè)簡(jiǎn)化版,但也是嚴(yán)格遵從她們項(xiàng)目的代碼結(jié)構(gòu)來(lái)實(shí)現(xiàn)的。
說(shuō)回 Springboot,毫無(wú)疑問(wèn),無(wú)論是天然自帶 Tomcat 或 Jetty ,還是和三方框架整合的各種 Starter 包,Springboot 都將開箱即用做到了極致,但是轉(zhuǎn)念又一想,其實(shí) Springboot 和 Gin 嚴(yán)格來(lái)說(shuō)做比較沒(méi)啥意義,就像 Java 和 Go 的比較一樣,我覺(jué)得也沒(méi)啥意義,各自的優(yōu)勢(shì)區(qū)間不一樣,并且各自也都在相關(guān)的領(lǐng)域叱咤風(fēng)云。
各位看官,你們覺(jué)得呢。