手把手教你前后分离架构(五) SpringBoot连接数据库 管理系统离不开关系数据库的支持, 数据库采用mysql数据库。

1、连接数据库

管理系统离不开关系数据库的支持, 数据库采用mysql数据库。

1.1、数据库创建

MySQL在5.5.3之后增加了utf8mb4的字符集,mb4就是most bytes 4的意思,专门用来兼容四字节的unicode。utf8mb4是utf8的超集,除了将编码改为utf8mb4外不需要做其他转换。 utf8mb4 是目前最大的一个字符编码,支持任意文字。

utf8mb4对应的排序字符集有utf8mb4_unicode_ci、utf8mb4_general_ci.
utf8mb4_unicode_ci是基于标准的Unicode来排序和比较,能够在各种语言之间精确排序.在特殊情况下,Unicode排序规则为了能够处理特殊字符的情况,实现了略微复杂的排序算法。
utf8mb4_general_ci没有实现Unicode排序规则,在遇到某些特殊语言或者字符集,排序结果可能不一致。
utf8mb4_unicode_ci 校对速度快,但准确度稍差。utf8_unicode_ci准确度高,但校对速度稍慢,两者都不区分大小写。通常情况下,新建数据库时一般选用 utf8mb4_general_ci 就可以了

//创建数据
CREATE DATABASE mir DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
//创建用户并授权
CREATE USER 'mir'@'%' IDENTIFIED BY '123456';
GRANT ALL PRIVILEGES ON mir.* TO 'mir'@'%';
FLUSH PRIVILEGES;

MySQL存储引擎

Innodb引擎:Innodb引擎提供了对数据库ACID事务的支持。并且还提供了行级锁和外键的约束。它的设计的目标就是处理大数据容量的数据库系统。

MyIASM引擎(原本Mysql的默认引擎):不提供事务的支持,也不支持行级锁和外键。

1.2、整合Mybaties-Plus

有很多持久层框架帮助我们更方便的操作数据库。常用的持久层框架有MyBatis 、hibernate等等。我们采用Mybatis-Plus作为系统持久层框架,Mybatis-Plus是一个Mybatis的增强工具,只是在Mybatis的基础上做了增强却不做改变,MyBatis-Plus支持所有Mybatis原生的特性,所以引入Mybatis-Plus不会对现有的Mybatis构架产生任何影响。Mybatis-Plus有依赖少、损耗小、预防Sql注入等诸多优点。

官网地址:安装 | MyBatis-Plus

1.2.2、依赖安装


 com.baomidou
 mybatis-plus-boot-starter
 3.4.1
 mysql
 mysql-connector-java
 8.0.25
 
 com.alibaba
 druid-spring-boot-starter
 1.1.10

1.2.2、添加配置

spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name= com.mysql.cj.jdbc.Driver
spring.datasource.url= jdbc:mysql://localhost:3306/mir
spring.datasource.username=mir
spring.datasource.password=123456

1.2.3、添加config配置

@Configuration
@MapperScan({"com.example.*.mapper"})
public class MyBatisPlusConfig {
}

如果配置全局扫描可以在Mapper接口添加   @Mapper 注解。

1.2.4、代码生成器

手动参照数据库字段编写对象实体等字段,很麻烦而且容易出错,MyBatis-Plus 代码生成器可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码,极大的提升了开发效率。

https://baomidou.com/pages/779a6e/#快速入门  参照官网

 mybatis-plus-generator 3.5.1 及其以上版本可使用新版本代码生成器。

添加依赖


 com.baomidou
 mybatis-plus-generator
 3.5.2
 org.freemarker
 freemarker
 2.3.31

样例

public static void main(String[] args) {
 String url = "jdbc:mysql://localhost:3306/mir";
 String username = "mir";
 String password = "123456";
 FastAutoGenerator.create(url, username, password)
 .globalConfig(builder -> {
 builder.author("dehuisun") // 设置作者
 .enableSwagger() // 开启 swagger 模式
 .fileOverride() // 覆盖已生成文件
 .outputDir("D://generator"); // 指定输出目录
 })
 .packageConfig(builder -> {
 builder.parent("com.xgg") // 设置父包名
 .moduleName("sys") // 设置父包模块名
 .pathInfo(Collections.singletonMap(OutputFile.xml, "D://generator")); // 设置mapperXml生成路径
 })
 .strategyConfig(builder -> {
 builder.addInclude("sys_user") // 设置需要生成的表名
 .addTablePrefix("t_", "c_"); // 设置过滤表前缀
 })
 .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
 .execute();
 }

1.3、数据持久化规范

为了更好研发建议迭代系统,所以我们需要制定一些研发规范来帮助我们快速迭代。

1.3.1、数据库建表规范

为数据库表添加一些公共字段,比如deleted(逻辑删除标识)、create_time、update_time、create_by、update_by等等来实现数据添加变更记录、逻辑删除、乐观锁等业务需求。帮助我们实现数据恢复及数据修改回溯。

1.3.2、自动填充公共字段

业务表中有create_time、update_time、create_by、update_by这四个字段,create_time、update_time要自动填充为当前时间,create_by、update_by自动填充为当前登录的用户ID。

配置类

@Bean
 public OptimisticLockerInterceptor mybatisPlusInterceptor() {
 return new OptimisticLockerInterceptor();
 }
 
 @Bean
 public MetaObjectHandler metaObjectHandler() {
 return new MetaObjectHandler() {
 @Override
 public void insertFill(MetaObject metaObject) {
 SysUser user = getUserId(metaObject);
 if (!Objects.isNull(user)) {
 this.strictInsertFill(metaObject, "createBy", Long.class, user.getId());
 }
 this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
 }
 
 @Override
 public void updateFill(MetaObject metaObject) {
 SysUser user = getUserId(metaObject);
 if (!Objects.isNull(user)) {
 this.strictUpdateFill(metaObject, "updateBy", Long.class, user.getId());
 }
 this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
 }
 
 private SysUser getUserId(MetaObject metaObject) {
 //自己认证框架获取登录用户的方法
 }
 };

1.3.3、逻辑删除

添加配置文件

mybatis-plus.global-config.db-config.logic-delete-field=deleted
mybatis-plus.global-config.db-config.logic-delete-value=1
mybatis-plus.global-config.db-config.logic-not-delete-value=0

实体注解

@TableLogic
private Integer deleted;

1.3.4抽象公共实体类

@Getter
public class BaseEntity {
 @TableLogic
 private Integer deleted;
 
 @TableField(fill = FieldFill.INSERT)
 private Long createUserId;
 
 @TableField(fill = FieldFill.UPDATE)
 private Long updateUserId;
 
 @TableField(fill = FieldFill.INSERT)
 private LocalDateTime createTime;
 
 @TableField(fill = FieldFill.UPDATE)
private LocalDateTime updateTime;
@TableLogic
private Integer deleted;
}

公共实体类只添加getter方法,防止手动set赋值字段。

继承公共实体:

还有乐观锁等特性,可以后续根据需求来实现。

2、用户管理功能实现

2.1、后端实现

2.1.1 分层规范

系统参照阿里分层规范

• Web 层:controller层主要是对访问控制进行转发,各类基本参数校验,或者不复用的业务简单处理等。

• Service 层:相对具体的业务逻辑服务层。

• Manager 层:通用业务处理层,它有如下特征:

1) 对第三方平台封装的层,预处理返回结果及转化异常信息,适配上层接口。

2) 对 Service 层通用能力的下沉,如缓存方案、中间件通用处理。

3) 与 DAO 层交互,对多个 DAO 的组合复用。

• DAO 层:数据访问Mapper层,与底层 MySQL、Oracle、Hbase、OB 等进行数据交互。

2.1.2、创建用户表

CREATE TABLE `sys_user` (
 `id` BIGINT NOT NULL,
 `account` VARCHAR(50) NOT NULL COMMENT '账号',
 `username` VARCHAR(240) NOT NULL COMMENT '用户名',
 `password` VARCHAR(100) NOT NULL COMMENT '密码',
 `salt` VARCHAR(64) DEFAULT NULL COMMENT '盐',
 `email` VARCHAR(64) DEFAULT NULL COMMENT '邮箱',
 `mobile` VARCHAR(64) DEFAULT NULL COMMENT '手机号',
 `status` SMALLINT(6) DEFAULT '0' COMMENT '状态(0:正常1:停用2:锁定)',
 `err_num` TINYINT DEFAULT NULL COMMENT '登录错误次数',
 `lock_time` DATETIME DEFAULT NULL COMMENT '锁定时间',
 `create_by` BIGINT UNSIGNED DEFAULT NULL COMMENT '创建者ID',
 `create_time` DATETIME DEFAULT NULL COMMENT '创建时间',
 `update_by` BIGINT UNSIGNED DEFAULT NULL COMMENT '修改人ID',
 `update_time` DATETIME DEFAULT NULL COMMENT '修改时间',
 `deleted` TINYINT DEFAULT '0' COMMENT '是否被删除(0:未删除,1:已删除)',
 PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4 COMMENT='用户管理'

2.1.3、代码生成

生成的代码拷贝至项目

2.1.4、接口实现

@RestController
@Api(tags = "用户管理服务")
@RequestMapping("/sys/user")
public class UserController {
 @Resource
 private IUserService userService;
 @GetMapping("/page")
 @ApiOperation(value = "用户列表分页查询")
 public Result getPageList(@Valid UserPageParam userParam) {
 Page page = new Page(userParam.getCurrent(), userParam.getSize());
 userService.pageList(page, userParam.getUsername());
 Page pageResult = CollectionUtils.page(page, UserVO.class);
 return Result.ok().info(pageResult);
 }
 @ApiOperation("用户查询")
 @GetMapping("/{id}")
 @ApiImplicitParam(name = "id", value = "用户ID", required = true, paramType = "path", dataType = "Long")
 public Result getInfo(@PathVariable("id") Long id) {
 User user = userService.getById(id);
 UserVO userVO = new UserVO();
 if(user!=null) {
 BeanUtils.copyProperties(user, userVO);
 }
 return Result.ok().info(userVO);
 }
 @ApiOperation("用户删除")
 @DeleteMapping("/{id}")
 @ApiImplicitParam(name = "id", value = "用户ID", required = true, paramType = "path", dataType = "Long")
 public Result delete(@PathVariable Long id) throws Exception {
// User user = (User) SecurityUtils.getSubject().getPrincipal();
// if (sysUser.getId().equals(id)) {
// return Result.error().message("不能删除当前登录用户");
// }
 userService.removeById(id);
 return Result.ok();
 }
 @ApiOperation("用户新增")
 @PostMapping("")
 public Result save(@Validated(AddGroup.class) @RequestBody UserParam userParam) {
 User sysUser = new User();
 sysUser.setAccount(userParam.getAccount());
 sysUser.setUsername(userParam.getUsername());
 List list = userService.list(new QueryWrapper(sysUser));
 if (list!=null&list.size() != 0) {
 return Result.error().message("该账号已存在!");
 }
 BeanUtils.copyProperties(userParam, sysUser);
 sysUser.setId(null);
 sysUser.setPassword(Constant.PASSWORD);
 userService.save(sysUser);
 return Result.ok();
 }
 @ApiOperation("用户更新")
 @PutMapping("")
 public Result update(@Validated(UpdateGroup.class) @RequestBody UserParam userParam) {
 User sysUser = new User();
 sysUser.setAccount(userParam.getAccount());
 sysUser.setUsername(userParam.getUsername());
 List list = userService.list(new QueryWrapper(sysUser));
 if (list!=null&list.size() != 0) {
 return Result.error().message("该账号已存在!");
 }
 sysUser = userService.getById(userParam.getId());
 BeanUtils.copyProperties(userParam, sysUser);
 userService.updateById(sysUser);
 return Result.ok();
 }
}

接口实现参数校验(分组校验等多种方式)、统一返回、每层实体参数规划。用户实体继承公共参数。

2.1.5 解决精度丢失问题

@Configuration
@EnableWebMvc
public class JacksonConfig implements WebMvcConfigurer {
 @Override
 public void configureMessageConverters(List

2.1.6参数序列化问题

@JsonIgnoreProperties(ignoreUnknown = true)

2.2、前端实现

2.2.1实现列表和详情页


 
 
 
 
 
 
 
 
 
 查询
 重置
 
 
 
 
 
 
 
 新增
 
 
 
 
 
 
 
 
 
 
 
 正常
 禁用
 
 
 
 
 
 编辑
 删除
 
 
 
 
 
 
 
import AddDialog from './user-addDialog'
import AddDrawer from './user-addDrawer'
import axios from 'axios'
import {formartDateTime} from '@/utils'
export default {
 data() {
 return {
 tableForm: {
 name: '',
 province: ''
 },
 tableData: {
 records: [],
 total: 0,
 size: 10,
 current: 1,
 pages: 1
 },
 tableDataLoading: false,
 tableDataSelections: [],
 }
 },
 components: {
 AddDialog,
 AddDrawer
 },
 methods: {
 // 获取数据列表
 getTableData() {
 this.tableForm.current = this.tableData.current;
 this.tableForm.size = this.tableData.size;
 this.tableDataLoading = true;
 axios.get('http://127.0.0.1:8888/sys/user/page',{
 params: this.tableForm
 }).then(({data}) => {
 if (data.success) {
 this.tableData = data.info;
 this.tableDataLoading = false
 } else {
 this.$message.error(data.message)
 }
 }).catch(() => {
 this.$message.error(this.tips.error);
 }
 )
 },
 // 每页数
 sizeChangeHandle(val) {
 this.tableData.size = val;
 this.getTableData()
 },
 // 当前页
 currentChangeHandle(val) {
 this.tableData.current = val;
 this.getTableData()
 },
 handleClick(row) {
 console.log(row);
 },
 onSubmit() {
 console.log('submit!');
 },
 doEdit(id){
 this.$nextTick(()=>{
 this.$refs.addDialog.init(id);
 })
 },
 // 删除
 doDel(id) {
 this.$confirm(this.tips.isSure, this.tips.tips, {}).then(() => {
 //防止表单重复提交
 this.$MessageBox.showLoading();
 axios.delete(`http://127.0.0.1:8888/sys/user/${id}`
 ).then(({data}) => {
 this.$MessageBox.hideLoading();
 this.getTableData();
 if (data.success) {
 this.$message.success(data.message)
 } else {
 this.$message.error(data.message)
 }
 }).catch(() => {
 this.$MessageBox.hideLoading()
 this.$message.error(this.tips.error);
 })
 })
 },
 doDrawer(id){
 this.$nextTick(()=>{
 this.$refs.addDrawer.init(id);
 })
 },
 //时间格式化
 formatterDateTime: function (row, column, cellValue, index) {
 return formartDateTime(cellValue)
 },
 },
 activated() {
 this.getTableData()
 },
 computed: {
 pageLayout() {
 if (this.$store.state.common.clientType === 'phone') return 'total, sizes, prev, pager, next'
 return 'total, sizes, prev, pager, next, jumper'
 }
 },
}

 
 
 
 用户维护
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 取 消
 确 定
 
 
import axios from "axios";
import {isEmail, isMobile} from '@/utils/validate'
export default {
 data() {
 var validateEmail = (rule, value, callback) => {
 if (value & !isEmail(value)) {
 callback(new Error('邮箱格式错误'))
 } else {
 callback()
 }
 }
 var validateMobile = (rule, value, callback) => {
 if (value && !isMobile(value)) {
 callback(new Error('手机号格式错误'))
 } else {
 callback()
 }
 }
 return {
 visible: false,
 dataForm: {
 id: '',
 account: '',
 username: '',
 email: '',
 mobile: '',
 status: ''
 },
 formLabelWidth: '120px',
 dataRule: {
 account: [
 {required: true, message: '账号不能为空', trigger: 'blur'},
 { min: 3, max: 5, message: '账号长度在 3 到 10 个字符', trigger: 'blur' }
 ],
 username: [
 {required: true, message: '用户名不能为空', trigger: 'blur'},
 { min: 2, max: 5, message: '用户名长度在 2 到 5 个字符', trigger: 'blur' }
 ],
 email: [
 { required: true, message: '邮箱不能为空', trigger: 'blur' },
 {validator: validateEmail, trigger: 'blur'}
 ],
 mobile: [
 { required: true, message: '手机号不能为空', trigger: 'blur' },
 {validator: validateMobile, trigger: 'blur'}
 ]
 }
 };
 },
 methods: {
 init(id){
 this.visible = true;
 this.dataForm.id = id || ''
 this.$nextTick(() => {
 this.$refs['dataForm'].resetFields()
 })
 if (this.dataForm.id) {
 axios.get(`http://127.0.0.1:8888/sys/user/${this.dataForm.id}`,
 ).then(({data}) => {
 if (data.success) {
 this.dataForm = data.info;
 }
 })
 }
 },
 doSubmit(){
 this.$refs['dataForm'].validate((valid) => {
 if (valid) {
 //防止表单重复提交
 this.$MessageBox.showLoading()
 if (this.dataForm.id) {
 axios.put('http://127.0.0.1:8888/sys/user', this.dataForm
 ).then(({data}) => {
 this.$MessageBox.hideLoading()
 if (data.success) {
 this.visible = false
 this.$emit('refreshDataList')
 this.$message.success(data.message)
 } else {
 this.$message.error(data.message)
 }
 }).catch(({data}) => {
 this.$MessageBox.hideLoading()
 this.$message.error(this.tips.error);
 })
 }else{
 axios.post('http://127.0.0.1:8888/sys/user', this.dataForm
 ).then(({data}) => {
 this.$MessageBox.hideLoading()
 this.visible = false
 this.$emit('refreshDataList')
 if (data.success) {
 this.$message.success(data.message)
 } else {
 this.$message.error(data.message)
 }
 }).catch(({data}) => {
 this.$MessageBox.hideLoading()
 this.$message.error(this.tips.error);
 })
 }
 }
 })
 }
 },
};

2.2.2添加路由

{path: '/user', name: 'user', component: _import('sys/user-list'),meta: {title:'用户管理',isTab:true}},

2.2.3、添加菜单

用户管理

2.2.4、添加常量类

tips.js

const error = "系统异常,请稍后重试"
export default{
 error
}

Main.js

import tips from "@/constants/tips.js"
Vue.prototype.tips = tips

2.2.5、测试

 

关注公众号”小猿架构“,发送 "前后分离架构" ,下载课程视频+课程源码+课件。

作者:dehuisun原文地址:https://blog.csdn.net/sundehui01/article/details/125404474

%s 个评论

要回复文章请先登录注册