MJGA,让Java再次伟大,开箱即用Springboot怕不怕Gin?

科技   2024-11-22 11:13   山西  
隔壁组的云计算零零后女同事,后文简称云女士,非说Go的Gin框架比Springboot更加的开箱即用,我心想在Java里面Springboot已经打遍天下无敌手,这份底蕴岂是Gin能比。

但是云女士突出一个执拗,非我要PK一把,PK内容就是她使用Gin,而我使用Springboot快速搭建一个简单的Crud工程,最后让其他同事来评判哪个更开箱即用。我毫不犹豫就答应了,作为搭建Springboot学习工程的资深Crud选手,咱这份底气还是有的。

云女士选择使用Gin + Gorm来搭建,而我原本想选择Springboot + MyBatis,后面转念一想,这MyBatis要写XML文件,指不定就因为这个被云女士嘲笑了,所以我把MyBatis替换为了MyBatis-Plus,这就足够的简洁了吧。

# 正文


一. 准备事项


既然是Crud工程,自然要准备好操作的表,我和云女士通过如下语句在各自的数据库中创建好了如下两张表。
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工程


云女士的工程结构如下所示。


云女士的go.mod文件内容如下所示。
module gobase
go 1.17
require ( github.com/gin-gonic/gin v1.6.0 github.com/jinzhu/gorm v1.9.16 github.com/sirupsen/logrus v1.9.3 github.com/spf13/cast v1.5.1)

云女士定义了两个结构体作为模型(Model),book.go文件内容如下所示。
package model
const ( 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文件内容如下所示。
package model
const ( 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}

云女士补充道,TableName() 方法是为模型指定对应的表名。

云女士为book表和people表分别定义了Dao接口,dao.go文件内容如下所示。
package dao
import "gobase/model"
type BookDao interface { AddBook(book *model.Book) error UpdateBook(book *model.Book) error DeleteBook(book *model.Book) error ListBookById(id uint) (*model.Book, error)}
type PeopleDao interface { AddPeople(book *model.People) error UpdatePeople(book *model.People) error DeletePeople(book *model.People) error ListPeopleById(id uint) (*model.People, error)}

BookDao接口对应的实现在book_dao_impl.go文件中,实现如下。
package dao
import ( "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.Book if listResult := b.DB.Where("id = ?", id).First(&book); listResult.Error != nil { return nil, listResult.Error } return &book, nil}

PeopleDao接口对应的实现在people_dao_impl.go文件中,实现如下。
package dao
import ( "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.People if listResult := b.DB.Where("id = ?", id).First(&people); listResult.Error != nil { return nil, listResult.Error } return &people, nil}

要操作数据库,肯定需要数据库连接,云女士将数据库连接的管理实现在了mysql_connection_pool.go文件中,内容如下所示。
package mysql
import ( "fmt" "github.com/jinzhu/gorm" "gobase/dao" "log" "time")
const ( UserName = "root" PassWord = "root" Host = "192.168.101.8" Port = 3306 Database = "gotest" MaxLifetime = 60 * time.Second MaxIdletime = 30 * time.Second MaxOpenconns = 6 MaxIdleconns = 2 Dialect = "mysql")
type DataSouce struct { db *gorm.DB}
func NewDataSource() *DataSouce { var db *gorm.DB
dsn := 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文件中,内容如下。
package adapter
import ( "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()}

其实除了绑定路由,云女士还在Init() 函数中进行了简单的服务注入,也就是创建数据库连接池,然后将数据库连接池给到对应的web服务。

云女士将操作book表对应的web服务写在了book_controller.go文件中,其实现如下所示。
package adapter
import ( "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.Book if 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.Book if 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.Book if 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表对应的web服务写在了people_controller.go文件中,其实现如下所示。
package adapter
import ( "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.People if 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.People if 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.People if 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)}

最后,云女士简单的展示了一下对book表和prople表的Crud操作。
book表和people表的增删改成功时返回内容如下所示。


book表和people表的查询成功时返回内容如下所示。


三. Springboot快速搭建Crud工程


云女士基于Gin和Gorm搭建的Crud工程,我看完后内心扑哧一笑:不过如此。
那现在该轮到我表演了。首先给出整个工程结构图如下所示。


POM文件内容如下所示。
<?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表对应的实体类Book和People,如下所示。
@Getter@Setterpublic class Book {
@TableField("id") private int id;
@TableField("b_name") private String bookName; @TableField("b_price") private float bookPrice;
}
@Getter@Setterpublic class People {
@TableField("id") private int id;
@TableField("p_name") private String peopleName; @TableField("p_age") private int peopleAge;
}

然后定义定义接口,如下所示。
@Mapperpublic interface BookMapper extends BaseMapper<Book> {}
@Mapperpublic interface PeopleMapper extends BaseMapper<People> {}

最后是对应的Controller实现,BookController实现如下。
@Slf4j@RestController@RequestMapping("/book")public class BookController {
@Autowired private 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实现如下所示。
@Slf4j@RestController@RequestMapping("/people")public class PeopleController {
@Autowired private 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("查询人物失败", e); return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR); } }
}

启动应用程序,book表的Crud操作结果如下所示。

prople表的Crud操作结果如下所示。


# 总结


我宣布,Springboot就是快速搭建Crud工程的神。

其实,在基于Gin和Gorm搭建Crud工程时,云女士还是写得复杂了一点,但是我有幸看过她们云平台的项目的代码,云女士写得也没毛病,虽然是个简化版,但也是严格遵从她们项目的代码结构来实现的。

说回Springboot,毫无疑问,无论是天然自带Tomcat或Jetty,还是和三方框架整合的各种Starter包,Springboot都将开箱即用做到了极致,但是转念又一想,其实Springboot和Gin严格来说做比较没啥意义,就像Java和Go的比较一样,我觉得也没啥意义,各自的优势区间不一样,并且各自也都在相关的领域叱咤风云。
作者:半夏之沫
链接:juejin.cn/post/7245942451105562685
对编程、职场感兴趣的同学,可以链接我,微信:coder301 拉你进入“程序员交流群”。
🔥东哥私藏精品 热门推荐🔥

东哥作为一名超级老码农,整理了全网最全《Java高级架构师资料合集》

资料包含了《IDEA视频教程》《最全Java面试题库》、最全项目实战源码及视频》及《毕业设计系统源码》总量高达 650GB 。全部免费领取!全面满足各个阶段程序员的学习需求。

Java面试那些事儿
回复 java ,领取Java面试题。分享AI编程,Java教程,Java面试辅导,Java编程视频,Java下载,Java技术栈,AI工具,Java开源项目,Java简历模板,Java招聘,Java实战,Java面试经验,IDEA教程。
 最新文章