很多网站的后端都是基于Spring boot - mybatis进行开发的。
本文以此技术栈进行一个功能模块的开发
以最常见的注册功能为例
全部用最新的版本进行开发
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>3.0.3version>
dependency>
<dependency>
<groupId>com.mysqlgroupId>
<artifactId>mysql-connector-jartifactId>
<scope>runtimescope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starter-testartifactId>
<version>3.0.3version>
<scope>testscope>
dependency>
本文开发的是注册模块,所以我们只会用一张表,其他模块思路与本文一致。
我们应该遵循:数据库 -> 持久层 -> 业务层 -> 表现层 -> 前端页面 这样的顺序进行开发。
功能上,我们应该先做基础功能,并遵循 增 -> 查 -> 删 -> 改的顺序来开发。
制作注册功能,本质是将用户的信息存放到数据库中。
我们建立一个数据库名为db
CREATE DATABASE db character SET utf8;
此时我们就需要考虑用户表中应该有哪些字段。
此处我们就选用:自增主键id,用户名username,密码password,性别sex,手机号phone。
后续可能会有一些补充
建表语句:
CREATE TABLE `user` (
`id` bigint NOT NULL,
`username` varchar(32) NOT NULL,
`password` varchar(32) NOT NULL,
`sex` int,
`phone` varchar(32) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
此处使用yml类型的配置文件
spring:
datasource:
url: jdbc:mysql://localhost:3306/db?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
username: root
password: 123456
此处开始我们代码的正式开发
我们在给数据库添加数据时,不能将字段全部用变量传入,这样很麻烦,也不方便后续的一些操作,所以我们会创建表对应的实体类对象
User
package com.angelday.test.entity;
public class User {
private Integer id;
private String username;
private String password;
private Integer age;
private String phone;
/*后面是构造方法,Setter、Getter、toString方法
由于可以使用IDEA自动生成,此处不再赘述*/
}
前面说过,注册的本质就是向数据库中添加一行数据
所以它的SQL语句是:
INSERT INTO db (username, password, age, phone) VALUES (值列表);
本项目使用的是mybatis进行开发
我们在根目录下创建一个包 mapper,并且在这个包下创建一个接口
package com.angelday.test.mapper;
import com.angelday.test.entity.User;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserMapper {
/**
* 添加用户
* @param user 添加来的user对象
* @return 受影响的行数
*/
Integer insertUser(User user);
}
并在resources包下创建一个包mapper用作UserMapper接口的映射文件
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.angelday.test.mapper.UserMapper">
<insert id="insertUser" useGeneratedKeys="true" keyProperty="uid">
INSERT INTO t_user(username, password, phone, sex)
VALUES (#{username}, #{password}, #{phone}, #{sex})
insert>
mapper>
只有这个配置文件SpringBoot并不能识别到它,所以我们应该在配置文件中指定扫描路径
mybatis:
mapper-locations: classpath:mapper/*.xml
昨晚持久层之后我们要利用Springboot提供的@SpringBootTest进行测试,测试一定不要省略,这样可以减小问题的范围。
package com.angelday.store;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class StoreApplicationTests {
@Autowired
private UserMapper userMapper
@Test
void userMapperTest(){
User user = new User();
user.setUsername("Zhangsan");
user.setPassword("123456");
user.setSex(1);//一般都是规定1是男0是女
user.setPhone("12332112321");
//该方法返回int类型的整数表示受影响的行数
int rows = insertUser(User user);
}
}
执行这个方法之后去数据库中查看信息,如果信息没有任何问题,即可执行下一步。
包结构一般为根目录下建一个service包
service包中有impl和ex两个包分别装Service的实现类与业务层中可能出现的异常。
我们的用户名并没有设置为唯一,所以没有常见的用户名重复异常,我们就建立一个最大的异常作为所有Service层中会发生的异常
package com.angelday.test.service.ex;
/**
* 业务异常基类,所有的业务层异常都继承这个异常
*/
public class ServiceException extends RuntimeException{
public ServiceException() {
super();
}
public ServiceException(String message) {
super(message);
}
public ServiceException(String message, Throwable cause) {
super(message, cause);
}
public ServiceException(Throwable cause) {
super(cause);
}
protected ServiceException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
我们在service包下建立一个UserService接口
package com.angelday.test.service;
import com.angelday.test.entity.User;
import org.springframework.stereotype.Service;
/**
* 用户模块
*/
@Service
public interface IUserService {
/**
* 用户注册
* @param user
*/
void reg(User user);
}
在现实项目中一般我们的表中会有创建人创建时间修改人修改时间等字段用来记录日志,
而这些日志信息前端不会主动传来,所以我们会在业务层中补充这些字段
package com.angelday.store.service.impl;
import ...
@Service
public class UserServiceImpl implements IUserService {
@Autowired
private UserMapper userMapper;
/**
* 用户注册
* @param user 表单内容
*/
@Override
public void reg(User user){
//将user中的信息完善如:创建人创建时间等
user.setCreatedUser(user.getUsername());
user.setModifiedUser(user.getUsername());
Date date = new Date();
user.setCreatedTime(date);
user.setModifiedTime(date);
//密码一般不会用明文的形式存储到数据库中,此处就用常见的md5加密,对密码加密
//md5加密
String password = user.getPassword();
password = DigestUtils.md5DigestAsHex(password.getBytes(StandardCharsets.UTF_8));
user.setPassword(password);
//执行成功返回1
Integer rows = userMapper.insertUser(user);
if (rows != 1){
throw new ServiceException("产生未知错误");
}
}
}
记住,一定要测试!一定要测试!一定要测试!!!
@SpringBootTest
class StoreApplicationTests {
@Autowired
private UserService userService;
@Test
void userServiceTest(){
User user = new User();
user.setUsername("Lisi");
user.setPassword("654321");
user.setSex(1);
user.setPhone("12332112321");
//该方法返回int类型的整数表示受影响的行数
int rows = reg(User user);
}
}
执行后检查数据库的数据是否正确
这里我们需要确定前端具体要访问什么路径,一般都是由项目经理提前订好了,此处我们就自定义一个user/reg
路径,前端传来User对象,请求方式为POST
请求参数:/user/reg
请求参数:User user
请求类型:POST
响应结果:Result< Void>
后端所有的响应结果都要封装到这个类中,它一般包含以下三个变量
1.状态码 2. 消息 3. 数据
在com.angelday.test.util
下建立Result
类
package com.angelday.test.util;
/**
* 统一返回结果类
*/
public class JsonResult<E> {
private Integer state;
private String message;
private E data;
//构造方法 & Getter和Setter方法
}
根据与前端的约定,我们创建UserController
类
@RestController
@RequestMapping("users")
public class UserController{
@Autowired
private UserService userService;
/**
* 用户注册
* @param user 表单内的注册信息
* @return 返回成功状态码
*/
@RequestMapping("reg")
public Result<Void> reg(User user){
userService.reg(user);
return new Result<>(200);
}
}
写到这里发现:
常量
。创建一个Controler的基类,所有的表现层都继承这个类,在这个类里我们进行集中处理异常,并且封装一些状态码和信息做为常量。
使用@ExceptionHandler可以集中处理异常
/**
* 控制类的基类,负责管理通用的操作与数据
*/
public class BaseController {
public static final int OK = 200;
/**
* 集中处理异常
* @param e 异常对象
* @return 返回的json格式数据
*/
@ExceptionHandler({ServiceException.class,FileUploadException.class})
public Result<Void> handlerException(Throwable e){
Result<Void> result = new Result(e);
if (e instanceof ServiceException){
result.setState(1000);
result.setMessage("在插入数据时发生未知错误");
}
}
}
最后我们让UserController继承BaseController
此处可以使用PostMan或者直接用浏览器访问该路径
localhost:8080/users/reg?username=zhangsan&password=123456
如果数据成功添加到数据库中,本模块开发完成
页面的组件此处不详细讲解,只是向大家说明,表单的id
为form-register
,提交表单数据的按钮id
为button-register
。
大家只需要根据自己的表单id与按钮id修改名称即可。
一般在body标签的结尾前添加script标签,用来添加函数
<body>
..........
<script>
//首先监听按钮是否被点击
//此处使用id选择器,并且为其添加click事件
$("#button-register").click(function (){
//按钮点击后发送AJAX请求
$.ajax({
url: "/users/reg",
type: "POST",
// 使用serialize()方法会自动将表单json数据转化为username=Xxx的形式
data: $("#form-register").serialize(),
dataType: "JSON",
//成功的回调函数
success: function (json) {
if (json.state == 200){
alert("注册成功");
} else {
alert("注册失败");
}
},
//失败的回调函数
error: function (xhr) {
alert("注册时产生未知错误,请稍后重新尝试!"+ xhr.status);
}
});
});
script>
body>
此处我们可以运行TestApplication主程序开启服务,并在页面填入数据进行提交,如果返回警告框:注册成功
,并且数据库中的数据没有异常,整个模块功能的开发阶段,完成!
如果有其他的问题欢迎私信我,后续有其他的优化:如:使用Redis、使用会话跟踪技术、拦截器/过滤器等,我会再创一个分栏。