接上篇《毕设利器,教你从零搭建一个有规范的spring boot项目【一】》
之前只是做了简单的访问测试,要作为一个完整的web项目,我们需要
- 解析用户发出的请求
- 拿到数据库中的数据
- 进行数据的运算
- 将数据呈现在用户面前
之前只是简单地了解了第一步,接下来要连接数据库,通常用的都是MySQL。
引入依赖
首先还是拿别人写好的代码,引入连接数据库所需要的依赖:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.11</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.21</version>
</dependency>
这里说一下这些依赖的作用,在学习Java Web的时候,我们连接数据库需要做下面几件事:
- 建立和数据库的连接
- 编写SQL查询我们想要的数据
- 关闭和数据库的连接
上面这些依赖的作用就是让你省去1、3两个步骤,专注于SQL的编写。
但还是要写一写配置文件的,声明你要连接哪个数据库,对应的账号密码是多少。
然后那些繁琐的连接操作,框架会帮你解决的。
注意,这里的驱动(即第二个依赖:mysql-connector-java)要看对应的mysql版本,我的mysql是8以上的,所以mysql驱动要用8.0.11的。
如果你的mysql是5.X的,建议去找5.X的mysql驱动。
我之前用的mysql驱动是5.X的,就会报错,因此这块要注意。
至于怎么查看自己的MySQL版本,可以百度一下。
5.X的驱动,也可以百度MySQL5.X的依赖。
编写配置文件
接下来在application.properties文件中编写对应的配置文件,此举在于告诉框架,我们要连接哪个数据库、对应的账号密码是多少,还有一些细小的配置。
有些spring boot项目用的是application.yml文件,但都是一个东西,只是语法不一样而已,这里用的是application.properties,它都会放在如下图的位置:
在application.properties中配置数据源DataSource,这里的配置根据自己的实际情况写
springboot数据源初始化
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/你的数据库名?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=false&allowPublicKeyRetrieval=true
spring.datasource.username=你的数据库用户名
spring.datasource.password=你的数据库密码
同样的,这里的url(即代码中的第三行)MySQL5.X的写法跟我8.X的写法是不一样的,同样需要你去百度。
只会比我上面这个写法简单。
MySQL建数据库的规范
说到这里顺便说一下MySQL建表的规范好了。
我用的是navicat,都是一样的。
现在建库的字符集都是用utf8mb4,听说可以存表情图标?
哈哈哈,细节不用管太多,通常都是用这个字符集的,可以解决很多问题。
建一个用户表(user)举个例子。
一个有规范的表,除了用户信息和主键,还应该有is_delete、create_time、update_time字段,这三个字段的作用分别是:
-
is_delete:1表示删除,0表示还没删除,通常我们很多的删除操作都不是真正的删除,因为如果你数据库学的还不错的话,你就知道如果要建立索引,数据就不能经常删除。此外不真正地删除数据后面说不定也能用上。格式用tinyint就行,横竖只会有1和0两个值。设置默认值为0就行,插入数据的时候可以不用管,MySQL会自动生成的。 -
create_time:创建时间,一般默认值设置为now()就行,插入的时候甚至不用管他,MySQL会自动生成的。 -
update_time:更新时间,通常我们也是默认值设置为now(),不过需要再勾选个【根据当前时间戳更新】,同样的,更新跟插入数据的时候也是不用管他,MySQL会自动生成的。
最好再给表写个注释,说明这个表是干嘛用的:
当然这些只是规范,公司不同,具体的字段名也会不同,但这三个字段基本都会有。
甚至于说,你不按我说的来也行。
只是,会看我这篇博客的通常都是刚入门的,还没工作的,我也是在网上看着各种教程自学过来的。
怎么说呢,能提前熟悉一下公司的规范也不是坏事,并且事情做的工整些,也是个好习惯,你说是不是?
而且这些确实有好处,小项目可能体现不出来,但有的时候找bug就是要靠这些字段来判断。
注释写的好,后面回来看也快。
如果你用的不是navicat,不知道上面一些操作在哪设置,你也可以复制我下面的建表SQL:
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`uuid` bigint(0) NOT NULL COMMENT '主键',
`name` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '用户名',
`is_delete` tinyint(1) NULL DEFAULT NULL COMMENT '1删除',
`create_time` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP(0),
`update_time` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP(0) ON UPDATE CURRENT_TIMESTAMP(0),
PRIMARY KEY (`uuid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '用户表' ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;
表建好了,那么让我们回到spring boot,继续我们的工作。
Java是面向对象的,通常我们需要个对象,来装我们从数据库中查出来的对象,这里也是有规范的。
model层
上篇文章已经说明了mvc,大概讲了c即controller,这里讲一下m即model,翻译过来就是模型,其实就是上面说的用来装数据的对象。
这些大体上分为po和vo:
- po:跟数据库里的字段一摸一样,不能有任何偏差。
- vo:展现到前端的数据,根据不同的业务展现字段。
以刚刚建的user表为例,我们要把数据放进去,就需要一个和数据库字段一摸一样的UserPO类来装数据。
在controller层的平级建一个model包: 再在model包下建一个po包: 然后建一个UserPO类,这个类的字段要求和数据库中的user表一摸一样:
package com.TandK.model.po;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.util.Date;
import lombok.Data;
@TableName(value ="user")
@Data
public class UserPO implements Serializable {
@TableId(value = "uuid")
private String uuid;
@TableField(value = "name")
private String name;
@TableField(value = "is_delete")
private Integer isDelete;
@TableField(value = "create_time")
private Date createTime;
@TableField(value = "update_time")
private Date updateTime;
@TableField(exist = false)
private static final long serialVersionUID = 1L;
}
这里解释一下类上的注解@Data,是lombok的注解,它会生成对应字段的getter/setter方法,这样你就可以不用自己写了。
@TableName(value =“user”)注解是告诉框架,这个类对应的是数据库中的哪个表。
@TableField()注解,可以看到每个字段都有,就是声明这个字段对应数据库中该表的哪个字段,很多情况下,数据库的注解是下划线的,而java通常用的是驼峰,比如上面的创建时间这个字段,在数据库中它是create_time,在java中它就是createTime,写下这个注解能解决很多问题。
如果字段保证一样,这个注解可以不写。
然后可以看到uuid字段,明明数据库里设置的bigint类型,到这里为什么改成了String类型了?
这是因为设置成bigint,19位长度的数字的话,在传送给前端的时候会丢失精度,用String的话的可以解决这个问题。
上面是po的一些规范,接下来说说vo的。
同样是user,需要在model包下建一个vo包,再在vo包下建一个UserVO类,注意和po是同级的: 为什么要这么麻烦呢?
因为有的时候一些字段是不需要暴露给前端的,我们自己用就行了,比如is_delete、create_time、update_time这些字段,当然具体的业务还会有不同。
那这里的user为例,那么UserVO的字段只需要下面这些:
package com.TandK.model.vo;
import lombok.Data;
@Data
public class UserVO {
private String uuid;
private String name;
}
view层
说完了mvc的m和c,顺便说说v好了,v即view层,就是视觉层,早些年项目都是前后端一起写的,现在前后端分离,现在view层不需要我们写了,交给专业的前端。
结合起来理解mvc,其实就是
- 用户在浏览器输入了一个链接,你的controller层会解析这个url,决定调用什么方法。
- 通常来说会需要去数据库里查数据,这个时候model层就起了作用了,首先用po装你刚查出来的数据,再转成vo,有选择地展现一些数据出去。
- 接下来再渲染view层,这样用户就看到了一个完整的网页了。
其实就是这么一个流程。
查询数据
数据库有了、装数据的容器对象也有了,接下来就是把查询到的数据返回给前端了。
我们把从数据库中查询数据的方法都放在mapper层中,也叫dao(Data Access Object,即数据连接对象)层。
这一层只执行数据库的增删改查操作。
新建一个mapper包,如下:
连接数据库的User表,需要一个UserMapper类:
package com.TandK.mapper;
import com.TandK.model.po.UserPO;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
public interface UserMapper extends BaseMapper<UserPO> {
}
建立完mapper包后,要在Application类中加上一个@MapperScan(“com.TandK.mapper”)注解,声明Mapper层所在的包:
package com.TandK;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@MapperScan("com.TandK.mapper")
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
然后你的具体业务逻辑需要写在service层,新建一个service包和service.impl包,在service包下建立UserService,在service.impl包下建立UserServiceImpl:
让UserServiceImpl实现UserService接口:
package com.TandK.service;
public interface UserService {
}
package com.TandK.service.impl;
import com.TandK.service.UserService;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
}
@Service注解会告诉Spring Boot框架,这是真正的Service实现类,深入讲的话会涉及到Spring的依赖注入了,这里先不深入讲,先学会用再去深入理解为什么吧,你可以理解为这里不加这个注解就会报错。
这些准备好了之后,我们做一个查询用户列表的操作:
首先在UserController类下写一个方法:
package com.TandK.controller;
import com.TandK.model.vo.UserVO;
import com.TandK.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("users")
public class UserController {
@Autowired
private UserService userService;
@GetMapping()
public List<UserVO> getUsers(){
return userService.getUsers();
}
}
仔细看你会发现UserService并没有初始化值。
正常情况下,我们要用UserService这个对象需要这么写:
private UserService userService = new UserServiceImpl();
毕竟UserServiceImpl才是真正的实现对象,UserService只是一个接口而已。
但是我们刚刚已经在UserServiceImpl类上加入了@Service注解,再在这个变量上面加了@Autowired注解。 这样框架就会自动给你生成一个实例对象。
这就是Spring之前经常拿出来吹的IoC,关于这点的细节可以看看我的《IoC和AOP》,但我建议等你真正会用SpringBoot之后再看。
Service层的方法如下:
package com.TandK.service;
import com.TandK.model.vo.UserVO;
import java.util.List;
public interface UserService {
List<UserVO> getUsers();
}
在实现类中实现上面的方法:
package com.TandK.service.impl;
import com.TandK.mapper.UserMapper;
import com.TandK.model.po.UserPO;
import com.TandK.model.vo.UserVO;
import com.TandK.service.UserService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.stream.Collectors;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public List<UserVO> getUsers() {
QueryWrapper wrapper = new QueryWrapper();
wrapper.eq("is_delete", 0);
List<UserPO> userPOS = userMapper.selectList(wrapper);
List<UserVO> userVOS = userPOS.stream().map(userPO -> {
UserVO userVO = new UserVO();
BeanUtils.copyProperties(userPO, userVO);
return userVO;
}).collect(Collectors.toList());
return userVOS;
}
}
详细说明下这个方法: 这用了插件的部分,其实就是拼装、并查询了下面这句SQL
SELECT * FROM user WHERE is_delete = 0
拼装和查询用的MyBatis-plus,它可以让你不用编写简单的sql,只用几个简单的方法就能拼装。
MyBatis-plus的依赖前面已经引入了,你也可以直接用。
但其实除了偷懒没什么叼用,因为还要让它拼装,所以执行效率我觉得还不如你自己手写的快。
美其名曰提高开发效率,其实我用的还是挺爽的,但是稍稍复杂点的SQL,比如连表查询,我觉得还是自己手写SQL吧,不止执行效率高,后面你回去改也不那么难受。
Service层负责查询数据、进行逻辑的编写,最简单的,比如你查出来的UserPO,有些字段比如is_delete、create_time这些,前端是完全不需要知道他们的存在的,那么你就可以转成VO再传出去。
然后启动项目,在数据库里随便插点数据,就可以在浏览器中访问到对应的数据了:
上面就是完成了一个最简单的查询动作。
我们搭建完项目之后,其实最主要的工作就是:
- 编写Controller层的方法,这部分的工作刚刚说过了,就是输入一个url,然后Controller层知道调用哪个方法,一般在Controller层的方法里会调用Service层的方法。
- Service层的方法是真正写业务逻辑的地方,它调用mapper层的方法增删改查数据库里的数据,然后做各种运算,返回想要的数据
- mapper层的方法很简单,就是简单的执行sql语句。
完成了一个最简单的查询动作之后,我们要做一下操作让这个项目变得规范起来。
上面这样也能用,倒不如说我做毕设的时候就是这样子开始写的,只不过接下来这些,有了规范,会有各个方面的好处,最直接的好处就是能提前熟悉外面公司的做法。
这个就下一篇写了。
有什么问题可以留言,看到了都会尽量帮忙解决。
|