Java精选面试题 (微信小程序): 5000+ 道面试题和选择题, 真实面经 , 简历模版 ,包含Java基础、并发、JVM、线程、MQ系列、Redis、Spring系列、Elasticsearch、Docker、K8s、Flink、Spark、架构设计、大厂真题等,在线随时刷题!
前言unset

隔壁组的云计算零零后女同事,后文简称 云女士 ,非说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工程

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

打开网易新闻 查看更多图片
Gin目录结构

云女士的 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 表的增删改成功时返回内容如下所示。

打开网易新闻 查看更多图片
Gin-增删改成功

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

打开网易新闻 查看更多图片
Gin-查询图书
打开网易新闻 查看更多图片
Gin-查询人物

Springboot快速搭建Crud工程

云女士基于 Gin 和 Gorm 搭建的 Crud 工程,我看完后内心扑哧一笑:不过如此。

那现在该轮到我表演了。首先给出整个工程结构图如下所示。

打开网易新闻 查看更多图片
Springboot目录结构

POM 文件内容如下所示。

  


        

 4.0.0 modelVersion>          

            

 org.springframework.boot groupId>           

 spring-boot-starter-parent artifactId>           

 2.4.1 version>        parent>          

 com.lee.javabase groupId>       

 javabase artifactId>       

 1.0-SNAPSHOT version>          

            

                

 org.springframework.boot groupId>               

 spring-boot-starter-web artifactId>            dependency>           

                

 org.springframework.boot groupId>               

 spring-boot-starter-jdbc artifactId>            dependency>              

                

 com.baomidou groupId>               

 mybatis-plus-boot-starter artifactId>               

 3.1.0 version>            dependency>           

                

 mysql groupId>               

 mysql-connector-java artifactId>               

 8.0.16 version>            dependency>              

                

 org.projectlombok groupId>               

 lombok artifactId>            dependency>        dependencies>      project>  


























同样,定义 book 表和 people 表对应的实体类 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

  {   }      @Mapper   public interface PeopleMapper extends BaseMapper

  {   }  

最后是对应的 Controller 实现, BookController 实现如下。

@Slf4j  
@RestController  
@RequestMapping("/book")  
public class BookController {  
  
    @Autowired  
    private BookMapper bookMapper;  
  
    @PostMapping("/add")  
    public ResponseEntity   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   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   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   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   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   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   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   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 的比较一样,我觉得也没啥意义,各自的优势区间不一样,并且各自也都在相关的领域叱咤风云。

各位看官,你们觉得呢。

作者:半夏之沫 来源:https://juejin.cn/post/7245942451105562685

公众号“Java精选”所发表内容注明来源的,版权归原出无法查证版权的或者未注明出处的均来自网络,系转载,转载的目的在于传递更多信息,版权属于原作者。如有侵权,请联系,笔者会第一时间删除处理!

最近有很多人问,有没有技术或摸鱼交流群!加入方式很简单,公众号Java精选,回复“加群”,即可入群!在线摸鱼:https://www.yoodb.com/

Java精选面试题(微信小程序):3000+道面试题,包含Java基础、并发、JVM、线程、MQ系列、Redis、Spring系列、Elasticsearch、Docker、K8s、Flink、Spark、架构设计等,在线随时刷题!

特别推荐:专注分享最前沿的技术与资讯,为弯道超车做好准备及各种开源项目与高效率软件的公众号,「大咖笔记」,专注挖掘好东西,非常值得大家关注。点击下方公众号卡片关注

文章有帮助的话,点在看,转发吧!