背景
笔记为学习青戈大佬 从0开始带你手撸一套SpringBoot+Vue后台管理系统(2022年最新版) 所总结笔记,瑞斯拜~
软件安装
项目所使用软件及版本如↓
软件包自取:
链接: https://pan.baidu.com/s/10UEXF10CuMSA8zFNrLBYBw?pwd=rb9d
提取码: rb9d
vue安装
还需要安装vue-cli(==这里和视频版本不一致,有问题检查版本==),教程戳vue-cli(vue脚手架)安装
详细教程
抄作业,步骤如下:
(1)命令行工具输入
安装完成后输入
检查
(2)命令行输入
1 npm install --global vue-cli
安装完成后输入
检查
tips:项目中根目录依据所建项目文件夹灵活变动,不一定和该文章保持完全一致
在F:/代码/后台管理系统 下新建vue项目
设置属性如下图,版本选择2.X,接下来分别选择Y→json→n
创建完毕:
idea打开刚新建项目vue
命令行中在F:/代码/后台管理系统/vue 下执行
本地访问 http://localhost:8080/能够访问到刚刚构建的前端项目
element框架安装
项目中后台管理系统使用element框架
element官方安装教程
F:/代码/后台管理系统/vue 下执行
安装完成后依据Quick
Start 指引
在main.js下引入element
1 2 3 4 5 6 7 8 9 10 11 12 13 import Vue from 'vue' import App from './App.vue' import router from './router' import ElementUI from 'element-ui'; import 'element-ui/lib/theme-chalk/index.css'; Vue.config.productionTip = false Vue.use(ElementUI, { size: "small" } ); new Vue({ router, render: h => h(App) } ).$mount('#app')
HomeView.vue下进行test
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <template> <div class="home"> <el-button>{{msg}}</el-button> <img alt="Vue logo" src="../assets/logo.png"> <HelloWorld msg="Welcome to Your Vue.js App"/> </div> </template> <script> // @ is an alias to /src import HelloWorld from '@/components/HelloWorld.vue' export default { name: 'HomeView', components: { HelloWorld }, data(){ return{ msg: "Hello Sliu" } } } </script>
前端页面修改
前端页面的编写快速带过,静态资源github自取
第一部分
main.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import Vue from 'vue' import App from './App.vue' import router from './router' import ElementUI from 'element-ui' ;import 'element-ui/lib/theme-chalk/index.css' ;Vue .config .productionTip = false Vue .use (ElementUI , { size : "small" });new Vue ({ router, render : h => h (App ) }).$mount('#app' )
Home.vu(第一节)
1 2 3 4 5 6 7 8 9 10 11 12 13 <el-button type="danger">{{ msg }}</el-button> export default { name: 'Home', components: { HelloWorld }, data() { return { msg: "hello 青哥哥" } } }
第二部分
Home.vue(第二节)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 <template> <div> <el-container> <el-aside width="200px" style="background-color: rgb(238, 241, 246);"> <el-menu :default-openeds="['1', '3']" style="min-height: 100%"> <el-submenu index="1"> <template slot="title"><i class="el-icon-message"></i>导航一</template> <el-menu-item-group> <template slot="title">分组一</template> <el-menu-item index="1-1">选项1</el-menu-item> <el-menu-item index="1-2">选项2</el-menu-item> </el-menu-item-group> <el-menu-item-group title="分组2"> <el-menu-item index="1-3">选项3</el-menu-item> </el-menu-item-group> <el-submenu index="1-4"> <template slot="title">选项4</template> <el-menu-item index="1-4-1">选项4-1</el-menu-item> </el-submenu> </el-submenu> <el-submenu index="2"> <template slot="title"><i class="el-icon-menu"></i>导航二</template> <el-menu-item-group> <template slot="title">分组一</template> <el-menu-item index="2-1">选项1</el-menu-item> <el-menu-item index="2-2">选项2</el-menu-item> </el-menu-item-group> <el-menu-item-group title="分组2"> <el-menu-item index="2-3">选项3</el-menu-item> </el-menu-item-group> <el-submenu index="2-4"> <template slot="title">选项4</template> <el-menu-item index="2-4-1">选项4-1</el-menu-item> </el-submenu> </el-submenu> <el-submenu index="3"> <template slot="title"><i class="el-icon-setting"></i>导航三</template> <el-menu-item-group> <template slot="title">分组一</template> <el-menu-item index="3-1">选项1</el-menu-item> <el-menu-item index="3-2">选项2</el-menu-item> </el-menu-item-group> <el-menu-item-group title="分组2"> <el-menu-item index="3-3">选项3</el-menu-item> </el-menu-item-group> <el-submenu index="3-4"> <template slot="title">选项4</template> <el-menu-item index="3-4-1">选项4-1</el-menu-item> </el-submenu> </el-submenu> </el-menu> </el-aside> <el-container> <el-header style="text-align: right; font-size: 12px; border-bottom: 1px solid #ccc; line-height: 60px"> <el-dropdown> <i class="el-icon-setting" style="margin-right: 15px"></i> <el-dropdown-menu slot="dropdown"> <el-dropdown-item>查看</el-dropdown-item> <el-dropdown-item>新增</el-dropdown-item> <el-dropdown-item>删除</el-dropdown-item> </el-dropdown-menu> </el-dropdown> <span>王小虎</span> </el-header> <el-main> <el-table :data="tableData"> <el-table-column prop="date" label="日期" width="140"> </el-table-column> <el-table-column prop="name" label="姓名" width="120"> </el-table-column> <el-table-column prop="address" label="地址"> </el-table-column> </el-table> </el-main> </el-container> </el-container> </div> </template> <script> export default { name: 'Home', data() { const item = { date: '2016-05-02', name: '王小虎', address: '上海市普陀区金沙江路 1518 弄' }; return { tableData: Array(20).fill(item), msg: "hello 青哥哥" } } } </script>
App.vue(第二节)
1 2 3 4 5 6 7 8 9 10 11 <template> <div id="app"> <router-view/> </div> </template> <style> #app{ min-height: 100vh; } </style>
gloable.css
1 2 3 4 5 html ,body ,div { margin : 0 ; padding : 0 ; box-sizing : border-box; }
在main.js 引入:import './assets/gloable.css'
第三部分
gloable.css
1 2 3 4 5 *{ margin : 0 ; padding : 0 ; box-sizing : border-box; }
App.vue
1 2 3 4 5 <template> <div id="app"> <router-view/> </div> </template>
Home.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 <template> <el-container style="min-height: 100vh"> <el-aside :width="sideWidth + 'px'" style="background-color: rgb(238, 241, 246); box-shadow: 2px 0 6px rgb(0 21 41 / 35%);"> <el-menu :default-openeds="['1', '3']" style="min-height: 100%; overflow-x: hidden" background-color="rgb(48, 65, 86)" text-color="#fff" active-text-color="#ffd04b" :collapse-transition="false" :collapse="isCollapse" > <div style="height: 60px; line-height: 60px; text-align: center"> <img src="../assets/logo.png" alt="" style="width: 20px; position: relative; top: 5px; margin-right: 5px"> <b style="color: white" v-show="logoTextShow">后台管理系统</b> </div> <el-submenu index="1"> <template slot="title"> <i class="el-icon-message"></i> <span slot="title">导航一</span> </template> <el-menu-item-group> <template slot="title">分组一</template> <el-menu-item index="1-1">选项1</el-menu-item> <el-menu-item index="1-2">选项2</el-menu-item> </el-menu-item-group> <el-menu-item-group title="分组2"> <el-menu-item index="1-3">选项3</el-menu-item> </el-menu-item-group> <el-submenu index="1-4"> <template slot="title">选项4</template> <el-menu-item index="1-4-1">选项4-1</el-menu-item> </el-submenu> </el-submenu> <el-submenu index="2"> <template slot="title"> <i class="el-icon-menu"></i> <span slot="title">导航二</span> </template> <el-menu-item-group> <template slot="title">分组一</template> <el-menu-item index="2-1">选项1</el-menu-item> <el-menu-item index="2-2">选项2</el-menu-item> </el-menu-item-group> <el-menu-item-group title="分组2"> <el-menu-item index="2-3">选项3</el-menu-item> </el-menu-item-group> <el-submenu index="2-4"> <template slot="title">选项4</template> <el-menu-item index="2-4-1">选项4-1</el-menu-item> </el-submenu> </el-submenu> <el-submenu index="3"> <template slot="title"> <i class="el-icon-setting"></i> <span slot="title">导航三</span> </template> <el-menu-item-group> <template slot="title">分组一</template> <el-menu-item index="3-1">选项1</el-menu-item> <el-menu-item index="3-2">选项2</el-menu-item> </el-menu-item-group> <el-menu-item-group title="分组2"> <el-menu-item index="3-3">选项3</el-menu-item> </el-menu-item-group> <el-submenu index="3-4"> <template slot="title">选项4</template> <el-menu-item index="3-4-1">选项4-1</el-menu-item> </el-submenu> </el-submenu> </el-menu> </el-aside> <el-container> <el-header style="font-size: 12px; border-bottom: 1px solid #ccc; line-height: 60px; display: flex"> <div style="flex: 1; font-size: 20px"> <span :class="collapseBtnClass" style="cursor: pointer" @click="collapse"></span> </div> <el-dropdown style="width: 70px; cursor: pointer"> <span>王小虎</span><i class="el-icon-arrow-down" style="margin-left: 5px"></i> <el-dropdown-menu slot="dropdown"> <el-dropdown-item>个人信息</el-dropdown-item> <el-dropdown-item>退出</el-dropdown-item> </el-dropdown-menu> </el-dropdown> </el-header> <el-main> <el-table :data="tableData"> <el-table-column prop="date" label="日期" width="140"> </el-table-column> <el-table-column prop="name" label="姓名" width="120"> </el-table-column> <el-table-column prop="address" label="地址"> </el-table-column> </el-table> </el-main> </el-container> </el-container> </template> <script> export default { name: 'Home', data() { const item = { date: '2016-05-02', name: '王小虎', address: '上海市普陀区金沙江路 1518 弄' }; return { tableData: Array(10).fill(item), msg: "hello 青哥哥", collapseBtnClass: 'el-icon-s-fold', isCollapse: false, sideWidth: 200, logoTextShow: true } }, methods: { collapse() { // 点击收缩按钮触发 this.isCollapse = !this.isCollapse if (this.isCollapse) { // 收缩 this.sideWidth = 64 this.collapseBtnClass = 'el-icon-s-unfold' this.logoTextShow = false } else { // 展开 this.sideWidth = 200 this.collapseBtnClass = 'el-icon-s-fold' this.logoTextShow = true } } } } </script>
第四部分
main.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import Vue from 'vue' import App from './App.vue' import router from './router' import ElementUI from 'element-ui' ;import 'element-ui/lib/theme-chalk/index.css' ;import './assets/gloable.css' Vue .config .productionTip = false Vue .use (ElementUI , { size : "mini" });new Vue ({ router, render : h => h (App ) }).$mount('#app' )
gloable.css
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 *{ margin : 0 ; padding : 0 ; box-sizing : border-box; } .ml-5 { margin-left : 5px ; } .mr-5 { margin-right : 5px ; } .pd-10 { padding : 10px 0 ; }
Home.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 <template> <el-container style="min-height: 100vh"> <el-aside :width="sideWidth + 'px'" style="box-shadow: 2px 0 6px rgb(0 21 41 / 35%);"> <el-menu :default-openeds="['1', '3']" style="min-height: 100%; overflow-x: hidden" background-color="rgb(48, 65, 86)" text-color="#fff" active-text-color="#ffd04b" :collapse-transition="false" :collapse="isCollapse" > <div style="height: 60px; line-height: 60px; text-align: center"> <img src="../assets/logo.png" alt="" style="width: 20px; position: relative; top: 5px; right: 5px"> <b style="color: white" v-show="logoTextShow">后台管理系统</b> </div> <el-submenu index="1"> <template slot="title"> <i class="el-icon-message"></i> <span slot="title">导航一</span> </template> <el-menu-item-group title="分组2"> <el-menu-item index="1-3">选项3</el-menu-item> </el-menu-item-group> <el-submenu index="1-4"> <template slot="title">选项4</template> <el-menu-item index="1-4-1">选项4-1</el-menu-item> </el-submenu> </el-submenu> <el-submenu index="2"> <template slot="title"> <i class="el-icon-menu"></i> <span slot="title">导航二</span> </template> <el-submenu index="2-4"> <template slot="title">选项4</template> <el-menu-item index="2-4-1">选项4-1</el-menu-item> </el-submenu> </el-submenu> <el-submenu index="3"> <template slot="title"> <i class="el-icon-setting"></i> <span slot="title">导航三</span> </template> <el-submenu index="3-4"> <template slot="title">选项4</template> <el-menu-item index="3-4-1">选项4-1</el-menu-item> </el-submenu> </el-submenu> </el-menu> </el-aside> <el-container> <el-header style="font-size: 12px; border-bottom: 1px solid #ccc; line-height: 60px; display: flex"> <div style="flex: 1; font-size: 20px"> <span :class="collapseBtnClass" style="cursor: pointer" @click="collapse"></span> </div> <el-dropdown style="width: 70px; cursor: pointer"> <span>王小虎</span><i class="el-icon-arrow-down" style="margin-left: 5px"></i> <el-dropdown-menu slot="dropdown" > <el-dropdown-item style="font-size: 14px; padding: 5px 0">个人信息</el-dropdown-item> <el-dropdown-item style="font-size: 14px; padding: 5px 0">退出</el-dropdown-item> </el-dropdown-menu> </el-dropdown> </el-header> <el-main> <div style="margin-bottom: 30px"> <el-breadcrumb separator="/"> <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item> <el-breadcrumb-item>用户管理</el-breadcrumb-item> </el-breadcrumb> </div> <div style="margin: 10px 0"> <el-input style="width: 200px" placeholder="请输入名称" suffix-icon="el-icon-search"></el-input> <el-input style="width: 200px" placeholder="请输入邮箱" suffix-icon="el-icon-message" class="ml-5"></el-input> <el-input style="width: 200px" placeholder="请输入地址" suffix-icon="el-icon-position" class="ml-5"></el-input> <el-button class="ml-5" type="primary">搜索</el-button> </div> <div style="margin: 10px 0"> <el-button type="primary">新增 <i class="el-icon-circle-plus-outline"></i></el-button> <el-button type="danger">批量删除 <i class="el-icon-remove-outline"></i></el-button> <el-button type="primary">导入 <i class="el-icon-bottom"></i></el-button> <el-button type="primary">导出 <i class="el-icon-top"></i></el-button> </div> <el-table :data="tableData" border stripe :header-cell-class-name="headerBg"> <el-table-column prop="date" label="日期" width="140"> </el-table-column> <el-table-column prop="name" label="姓名" width="120"> </el-table-column> <el-table-column prop="address" label="地址"> </el-table-column> <el-table-column label="操作" width="200" align="center"> <template slot-scope="scope"> <el-button type="success">编辑 <i class="el-icon-edit"></i></el-button> <el-button type="danger">删除 <i class="el-icon-remove-outline"></i></el-button> </template> </el-table-column> </el-table> <div style="padding: 10px 0"> <el-pagination :page-sizes="[5, 10, 15, 20]" :page-size="10" layout="total, sizes, prev, pager, next, jumper" :total="400"> </el-pagination> </div> </el-main> </el-container> </el-container> </template> <script> export default { name: 'Home', data() { const item = { date: '2016-05-02', name: '王小虎', address: '上海市普陀区金沙江路 1518 弄' }; return { tableData: Array(10).fill(item), msg: "hello 青哥哥", collapseBtnClass: 'el-icon-s-fold', isCollapse: false, sideWidth: 200, logoTextShow: true, headerBg: 'headerBg' } }, methods: { collapse() { // 点击收缩按钮触发 this.isCollapse = !this.isCollapse if (this.isCollapse) { // 收缩 this.sideWidth = 64 this.collapseBtnClass = 'el-icon-s-unfold' this.logoTextShow = false } else { // 展开 this.sideWidth = 200 this.collapseBtnClass = 'el-icon-s-fold' this.logoTextShow = true } } } } </script> <style> .headerBg { background: #eee!important; } </style>
效果图
SpringBoot框架搭建
新建spring项目
导入插件
application.properties下配置数据源
1 2 3 4 spring.datasource.driver-class-name =com.mysql.cj.jdbc.Driver spring.datasource.url =jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2b8 spring.datasource.username =root spring.datasource.password =123456
打开Navicat新建数据库sliu(数据库名随意,无须保持一致)
修改application.properties数据源
1 spring.datasource.url =jdbc:mysql://localhost:3306/sliu?serverTimezone=GMT%2b8
由于前端页面下,构建vue项目时所使用端口号也是8080,在application.properties下修改spring项目端口号为9090,以避免冲突
添加vue的启动方式
配置完成后前后端项目能够实现同时启动
SpringBoot集成Mybatis
application.properties中存在大量重复项,新建application.yaml替换它
1 2 3 4 5 6 7 8 9 server: port: 9090 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/sliu?serverTimezone=GMT%2b8 username: root password: 123456
新建user表用于测试数据库连接
表字段如下:
其中create_time默认值设置为CURRENT_TIMESTAMP
新增列
新建一个entity 目录,该目录下用于封装实体类 ,新建User类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package com.sliu.springboot.entity;import javax.xml.crypto.Data;@lombok .Datapublic class User { private Integer id; private String username; private String password; private String nickname; private String email; private String phone; private String address; private Data createTime; }
新建mapper 目录,该目录下保存数据库访问接口 ,新建UserMapper接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package com.sliu.springboot.mapper;import com.sliu.springboot.entity.User;import org.apache.ibatis.annotations.Mapper;import org.apache.ibatis.annotations.Select;import java.util.List;@Mapper public interface UserMapper { @Select("SELECT * from user") List<User> findAllUser () ; }
其中@Mapper接口用于将接口注入到容器中,用于完成后续自动注入
新建controller 文件夹,该文件夹用于处理前台发送的请求 。新建UserController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 package com.sliu.springboot.controller;import com.sliu.springboot.entity.User;import com.sliu.springboot.mapper.UserMapper;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RestController;import java.util.List;@RestController public class UserController { @Autowired UserMapper userMapper; @GetMapping("/") public List<User> printAllUser () { return userMapper.findAllUser(); } }
修改SpringbootApplication用于测试数据库查询是否成功
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package com.sliu.springboot;import com.sliu.springboot.entity.User;import com.sliu.springboot.mapper.UserMapper;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;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 @SpringBootApplication public class SpringbootApplication { @Autowired private UserMapper userMapper; public static void main (String[] args) { SpringApplication.run(SpringbootApplication.class, args); } @GetMapping("/") public List<User> toIndex () { return userMapper.findAllUser(); } }
修改SpringbootApplication
1 2 3 4 5 6 7 8 9 10 11 12 13 package com.sliu.springboot;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.web.bind.annotation.RestController;@RestController @SpringBootApplication public class SpringbootApplication { public static void main (String[] args) { SpringApplication.run(SpringbootApplication.class, args); } }
rebuild项目,访问http://localhost:9090
SpringBoot增删改查
通常,查询 数据库使用get 请求,而对于数据库的修改,例如增删改 使用post 请求
修改UserController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 package com.sliu.springboot.controller;import com.sliu.springboot.entity.User;import com.sliu.springboot.mapper.UserMapper;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.*;import java.util.List;@RestController @RequestMapping("/user") public class UserController { @Autowired UserMapper userMapper; @PostMapping public Integer save (@RequestBody User user) { return userMapper.insertUser(user); } @GetMapping public List<User> printAllUser () { return userMapper.findAllUser(); } }
修改UserMapper
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 package com.sliu.springboot.mapper;import com.sliu.springboot.entity.User;import org.apache.ibatis.annotations.Insert;import org.apache.ibatis.annotations.Mapper;import org.apache.ibatis.annotations.Select;import java.util.List;@Mapper public interface UserMapper { @Select("SELECT * from user") List<User> findAllUser () ; @Insert("Insert into user(username, password, nickname, email, phone, address) " + "VALUES(#{username}, #{password}, #{nickname}, #{email}, #{phone}, #{address})") int insertUser (User user) ; }
rebuild项目
打开postman ,发送post请求用于测试
继续改进UserController,当前save()方法下仅能够实现插入一条新数据,实际应用场景下,应当先判断该条id的数据在数据库中是否以及存在。不存在时插入,存在时应当执行的是更新操作。
新建service 文件夹,存放业务逻辑操作 ,并将操作完成的数据返回给view层,展现给用户。新建UserService
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 package com.sliu.springboot.service;import com.sliu.springboot.entity.User;import com.sliu.springboot.mapper.UserMapper;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;@Service public class UserService { @Autowired UserMapper userMapper; public int save (User user) { if (user.getId() == null ){ return userMapper.insertUser(user); }else { return userMapper.updateUser(user); } } }
修改UserMapper,新增update方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package com.sliu.springboot.mapper;import com.sliu.springboot.entity.User;import org.apache.ibatis.annotations.Insert;import org.apache.ibatis.annotations.Mapper;import org.apache.ibatis.annotations.Select;import org.apache.ibatis.annotations.Update;import java.util.List;@Mapper public interface UserMapper { @Select("SELECT * from user") List<User> findAllUser () ; @Insert("Insert into user(username, password, nickname, email, phone, address) " + "VALUES(#{username}, #{password}, #{nickname}, #{email}, #{phone}, #{address})") int insertUser (User user) ; @Update("update user set username=#{username}, password=#{password}, nickname=#{nickname}, email=#{email}, " + "phone=#{phone}, address=#{address} where id=#{id}") int updateUser (User user) ; }
修改UserController中save方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 package com.sliu.springboot.controller;import com.sliu.springboot.entity.User;import com.sliu.springboot.mapper.UserMapper;import com.sliu.springboot.service.UserService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.*;import java.util.List;@RestController @RequestMapping("/user") public class UserController { @Autowired UserMapper userMapper; @Autowired UserService userService; @PostMapping public Integer save (@RequestBody User user) { return userService.save(user); } @GetMapping public List<User> printAllUser () { return userMapper.findAllUser(); } }
rebuild项目并利用postman发送请求,测试
这样存在的问题是,在执行更新操作时,update
SQL语句下更新的是全部字段,当仅仅想要修改数据库的一个字段时,需要传入所有的实体类字段,否则会将其它字段全部置为Null(例如欲修改用户jjwang的邮箱时,post请求仅仅传入一个email字段,那么其它字段会全部置为Null)
动态SQL 就用于解决该类问题
安装MyBatisX插件
在resources文件夹下新建mapper/User.xml用于封装动态数据库
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace ="com.sliu.springboot.mapper.UserMapper" > <update id ="updateUser" > update user <set > <if test ="username != null" > username=#{username}, </if > <if test ="password != null" > password=#{password}, </if > <if test ="nickname != null" > nickname=#{nickname}, </if > <if test ="email != null" > email=#{email}, </if > <if test ="phone != null" > phone=#{phone}, </if > <if test ="address != null" > address=#{address} </if > </set > <where > id=#{id} </where > </update > </mapper >
修改UserMapper
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package com.sliu.springboot.mapper;import com.sliu.springboot.entity.User;import org.apache.ibatis.annotations.Insert;import org.apache.ibatis.annotations.Mapper;import org.apache.ibatis.annotations.Select;import org.apache.ibatis.annotations.Update;import java.util.List;@Mapper public interface UserMapper { @Select("SELECT * from user") List<User> findAllUser () ; @Insert("Insert into user(username, password, nickname, email, phone, address) " + "VALUES(#{username}, #{password}, #{nickname}, #{email}, #{phone}, #{address})") int insertUser (User user) ; int updateUser (User user) ; }
application.yml文件下引入该mapper,同时配置打印日志
1 2 3 4 5 6 7 8 9 10 11 12 13 server: port: 9090 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/sliu?serverTimezone=GMT%2b8 username: root password: 123456 mybatis: mapper-locations: classpath:mapper/*.xml configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
rebuild项目,postman下进行test
CRUD还有删除没有实现
修改UserController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 package com.sliu.springboot.controller;import com.sliu.springboot.entity.User;import com.sliu.springboot.mapper.UserMapper;import com.sliu.springboot.service.UserService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.*;import java.util.List;@RestController @RequestMapping("/user") public class UserController { @Autowired UserMapper userMapper; @Autowired UserService userService; @PostMapping public Integer save (@RequestBody User user) { return userService.save(user); } @GetMapping public List<User> printAllUser () { return userMapper.findAllUser(); } @DeleteMapping("/{id}") public Integer deleteUserById (@PathVariable Integer id) { return userMapper.deleteUserById(id); } }
修改UserMapper
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package com.sliu.springboot.mapper;import com.sliu.springboot.entity.User;import org.apache.ibatis.annotations.*;import java.util.List;@Mapper public interface UserMapper { @Select("SELECT * from user") List<User> findAllUser () ; @Insert("Insert into user(username, password, nickname, email, phone, address) " + "VALUES(#{username}, #{password}, #{nickname}, #{email}, #{phone}, #{address})") int insertUser (User user) ; int updateUser (User user) ; @Delete("delete from user where id=#{id}") int deleteUserById (@Param("id") Integer id) ; }
rebuild项目,postman下进行test
SpringBoot实现分页查询
分页查询的实质就是利用SQL语句中Limit关键字完成对数据的分割查询,以一个简单的例子入手
修改UserController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 package com.sliu.springboot.controller;import com.sliu.springboot.entity.User;import com.sliu.springboot.mapper.UserMapper;import com.sliu.springboot.service.UserService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.*;import java.util.List;@RestController @RequestMapping("/user") public class UserController { @Autowired UserMapper userMapper; @Autowired UserService userService; @PostMapping public Integer save (@RequestBody User user) { return userService.save(user); } @GetMapping public List<User> printAllUser () { return userMapper.findAllUser(); } @DeleteMapping("/{id}") public Integer deleteUserById (@PathVariable Integer id) { return userMapper.deleteUserById(id); } @GetMapping("/page") public List<User> printUserByPage (@RequestParam Integer pageNum, @RequestParam Integer pageSize) { pageNum = (pageNum - 1 ) * pageSize; return userMapper.printUserByPage(pageNum, pageSize); } }
修改UserMapper
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 package com.sliu.springboot.mapper;import com.sliu.springboot.entity.User;import org.apache.ibatis.annotations.*;import java.util.List;@Mapper public interface UserMapper { @Select("SELECT * from user") List<User> findAllUser () ; @Insert("Insert into user(username, password, nickname, email, phone, address) " + "VALUES(#{username}, #{password}, #{nickname}, #{email}, #{phone}, #{address})") int insertUser (User user) ; int updateUser (User user) ; @Delete("delete from user where id=#{id}") Integer deleteUserById (@Param("id") Integer id) ; @Select("SELECT * from user limit #{pageNum}, #{pageSize}") List<User> printUserByPage (Integer pageNum, Integer pageSize) ; }
rebuild项目,postman下进行test
除了依据PageNum和PageSize返回所对应页码的数据,还应当获取当前表格下全部数据条数,以便于通过计算得到总页数。以Map结构同时保存数据data,和总数据条数totalNum
修改UserController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 package com.sliu.springboot.controller;import com.sliu.springboot.entity.User;import com.sliu.springboot.mapper.UserMapper;import com.sliu.springboot.service.UserService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.*;import java.util.HashMap;import java.util.List;import java.util.Map;@RestController @RequestMapping("/user") public class UserController { @Autowired UserMapper userMapper; @Autowired UserService userService; @PostMapping public Integer save (@RequestBody User user) { return userService.save(user); } @GetMapping public List<User> printAllUser () { return userMapper.findAllUser(); } @DeleteMapping("/{id}") public Integer deleteUserById (@PathVariable Integer id) { return userMapper.deleteUserById(id); } @GetMapping("/page") public Map<String, Object> printUserByPage (@RequestParam Integer pageNum, @RequestParam Integer pageSize) { pageNum = (pageNum - 1 ) * pageSize; List<User> data = userMapper.printUserByPage(pageNum, pageSize); Integer totalUserNum = userMapper.selectTotal(); Map<String, Object> res = new HashMap <>(); res.put("data" , userMapper.printUserByPage(pageNum, pageSize)); res.put("total" , totalUserNum); return res; } }
修改userMapper
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 package com.sliu.springboot.mapper;import com.sliu.springboot.entity.User;import org.apache.ibatis.annotations.*;import java.util.List;@Mapper public interface UserMapper { @Select("SELECT * from user") List<User> findAllUser () ; @Insert("Insert into user(username, password, nickname, email, phone, address) " + "VALUES(#{username}, #{password}, #{nickname}, #{email}, #{phone}, #{address})") int insertUser (User user) ; int updateUser (User user) ; @Delete("delete from user where id=#{id}") Integer deleteUserById (@Param("id") Integer id) ; @Select("SELECT * from user limit #{pageNum}, #{pageSize}") List<User> printUserByPage (Integer pageNum, Integer pageSize) ; @Select("SELECT count(*) from user") Integer selectTotal () ; }
rebuild项目,postman下test,此时返回数据中包含有数据库总条数的信息
前端页面项目部署在8080端口,而后台服务则在9090端口,当前端页面要获取后台数据用于展示时,会出现跨域问题
SpringBoot项目下新建config 文件夹,新建CorsConfig
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 package com.sliu.springboot.config;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.web.cors.CorsConfiguration;import org.springframework.web.cors.CorsConfigurationSource;import org.springframework.web.cors.UrlBasedCorsConfigurationSource;import org.springframework.web.filter.CorsFilter;@Configuration public class CorsConfig { private static final long MAX_AGE = 24 * 60 * 60 ; @Bean public CorsFilter corsFilter () { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource (); CorsConfiguration corsConfiguration = new CorsConfiguration (); corsConfiguration.addAllowedOrigin("http://localhost:8080" ); corsConfiguration.addAllowedHeader("*" ); corsConfiguration.addAllowedMethod("*" ); corsConfiguration.setMaxAge(MAX_AGE); source.registerCorsConfiguration("/**" , corsConfiguration); return new CorsFilter (source); } }
User下对password属性添加注解,防止密码外泄
1 2 3 4 5 6 7 8 9 10 11 12 @lombok .Datapublic class User { private Integer id; private String username; @JsonIgnore private String password; private String nickname; private String email; private String phone; private String address; private Data createTime; }
将数据渲染到前端页面Home.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 <template> <el-container style="min-height: 100vh"> <el-aside :width="sideWidth + 'px'" style="box-shadow: 2px 0 6px rgb(0 21 41 / 35%);"> <el-menu :default-openeds="['1', '3']" style="min-height: 100%; overflow-x: hidden" background-color="rgb(48, 65, 86)" text-color="#fff" active-text-color="#ffd04b" :collapse-transition="false" :collapse="isCollapse" > <div style="height: 60px; line-height: 60px; text-align: center"> <img src="../assets/logo.png" alt="" style="width: 20px; position: relative; top: 5px; right: 5px"> <b style="color: white" v-show="logoTextShow">后台管理系统</b> </div> <el-submenu index="1"> <template slot="title"> <i class="el-icon-message"></i> <span slot="title">导航一</span> </template> <el-menu-item-group title="分组2"> <el-menu-item index="1-3">选项3</el-menu-item> </el-menu-item-group> <el-submenu index="1-4"> <template slot="title">选项4</template> <el-menu-item index="1-4-1">选项4-1</el-menu-item> </el-submenu> </el-submenu> <el-submenu index="2"> <template slot="title"> <i class="el-icon-menu"></i> <span slot="title">导航二</span> </template> <el-submenu index="2-4"> <template slot="title">选项4</template> <el-menu-item index="2-4-1">选项4-1</el-menu-item> </el-submenu> </el-submenu> <el-submenu index="3"> <template slot="title"> <i class="el-icon-setting"></i> <span slot="title">导航三</span> </template> <el-submenu index="3-4"> <template slot="title">选项4</template> <el-menu-item index="3-4-1">选项4-1</el-menu-item> </el-submenu> </el-submenu> </el-menu> </el-aside> <el-container> <el-header style="font-size: 12px; border-bottom: 1px solid #ccc; line-height: 60px; display: flex"> <div style="flex: 1; font-size: 20px"> <span :class="collapseBtnClass" style="cursor: pointer" @click="collapse"></span> </div> <el-dropdown style="width: 70px; cursor: pointer"> <span>王小虎</span><i class="el-icon-arrow-down" style="margin-left: 5px"></i> <el-dropdown-menu slot="dropdown"> <el-dropdown-item style="font-size: 14px; padding: 5px 0">个人信息</el-dropdown-item> <el-dropdown-item style="font-size: 14px; padding: 5px 0">退出</el-dropdown-item> </el-dropdown-menu> </el-dropdown> </el-header> <el-main> <div style="margin-bottom: 30px"> <el-breadcrumb separator="/"> <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item> <el-breadcrumb-item>用户管理</el-breadcrumb-item> </el-breadcrumb> </div> <div style="margin: 10px 0"> <el-input style="width: 200px" placeholder="请输入名称" suffix-icon="el-icon-search"></el-input> <el-input style="width: 200px" placeholder="请输入邮箱" suffix-icon="el-icon-message" class="ml-5"></el-input> <el-input style="width: 200px" placeholder="请输入地址" suffix-icon="el-icon-position" class="ml-5"></el-input> <el-button class="ml-5" type="primary">搜索</el-button> </div> <div style="margin: 10px 0"> <el-button type="primary">新增 <i class="el-icon-circle-plus-outline"></i></el-button> <el-button type="danger">批量删除 <i class="el-icon-remove-outline"></i></el-button> <el-button type="primary">导入 <i class="el-icon-bottom"></i></el-button> <el-button type="primary">导出 <i class="el-icon-top"></i></el-button> </div> <el-table :data="tableData" border stripe :header-cell-class-name="headerBg"> <el-table-column prop="id" label="ID" width="80"> </el-table-column> <el-table-column prop="username" label="用户名" width="140"> </el-table-column> <el-table-column prop="nickname" label="昵称" width="120"> </el-table-column> <el-table-column prop="email" label="邮箱"> </el-table-column> <el-table-column prop="phone" label="电话"> </el-table-column> <el-table-column prop="address" label="地址"> </el-table-column> <el-table-column label="操作" width="200" align="center"> <template slot-scope="scope"> <el-button type="success">编辑 <i class="el-icon-edit"></i></el-button> <el-button type="danger">删除 <i class="el-icon-remove-outline"></i></el-button> </template> </el-table-column> </el-table> <div style="padding: 10px 0"> <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="pageNum" :page-sizes="[2, 5, 10, 20]" :page-size="pageSize" layout="total, sizes, prev, pager, next, jumper" :total="total"> </el-pagination> </div> </el-main> </el-container> </el-container> </template> <script> // @ is an alias to /src import HelloWorld from '@/components/HelloWorld.vue' export default { name: 'Home', data() { return { tableData: [], total: 0, pageNum: 1, pageSize: 2, msg: "hello sliu", collapseBtnClass: 'el-icon-s-fold', isCollapse: false, sideWidth: 200, logoTextShow: true, headerBg: 'headerBg' } }, created() { //请求分页查询数据 fetch( "http://localhost:9090/user/page?pageNum="+this.pageNum+"&pageSize="+this.pageSize) .then(res => res.json()).then(res => { console.log(res) this.tableData=res.data this.total=res.total }) }, methods: { collapse() { // 点击收缩按钮触发 this.isCollapse = !this.isCollapse if (this.isCollapse) { // 收缩 this.sideWidth = 64 this.collapseBtnClass = 'el-icon-s-unfold' this.logoTextShow = false } else { // 展开 this.sideWidth = 200 this.collapseBtnClass = 'el-icon-s-fold' this.logoTextShow = true } }, load(){ fetch( "http://localhost:9090/user/page?pageNum="+this.pageNum+"&pageSize="+this.pageSize) .then(res => res.json()).then(res => { console.log(res) this.tableData=res.data this.total=res.total }) }, handleSizeChange(pageSize){ console.log(pageSize) this.pageSize = pageSize this.load() }, handleCurrentChange(pageNum){ console.log(pageNum) this.pageNum = pageNum this.load() } } } </script> <style> .headerBg { background: #eee !important; } </style>
rebuild前后端项目,访问localhost:8080,搞定√
基于以上查询实现以username的模糊查询
修改UserController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 package com.sliu.springboot.controller;import com.sliu.springboot.entity.User;import com.sliu.springboot.mapper.UserMapper;import com.sliu.springboot.service.UserService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.*;import java.util.HashMap;import java.util.List;import java.util.Map;@RestController @RequestMapping("/user") public class UserController { @Autowired UserMapper userMapper; @Autowired UserService userService; @PostMapping public Integer save (@RequestBody User user) { return userService.save(user); } @GetMapping public List<User> printAllUser () { return userMapper.findAllUser(); } @DeleteMapping("/{id}") public Integer deleteUserById (@PathVariable Integer id) { return userMapper.deleteUserById(id); } @GetMapping("/page") public Map<String, Object> printUserByPage (@RequestParam Integer pageNum, @RequestParam Integer pageSize, @RequestParam String username) { pageNum = (pageNum - 1 ) * pageSize; List<User> data = userMapper.printUserByPage(pageNum, pageSize, username); Integer totalUserNum = userMapper.selectTotal(username); Map<String, Object> res = new HashMap <>(); res.put("data" , data); res.put("total" , totalUserNum); return res; } }
修改UserMapper
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 package com.sliu.springboot.mapper;import com.sliu.springboot.entity.User;import org.apache.ibatis.annotations.*;import java.util.List;@Mapper public interface UserMapper { @Select("SELECT * from user") List<User> findAllUser () ; @Insert("Insert into user(username, password, nickname, email, phone, address) " + "VALUES(#{username}, #{password}, #{nickname}, #{email}, #{phone}, #{address})") int insertUser (User user) ; int updateUser (User user) ; @Delete("delete from user where id=#{id}") Integer deleteUserById (@Param("id") Integer id) ; @Select("SELECT * from user where username like concat('%', #{username}, '%') limit #{pageNum}, #{pageSize}") List<User> printUserByPage (Integer pageNum, Integer pageSize, String username) ; @Select("SELECT count(*) from user where username like concat('%', #{username}, '%')") Integer selectTotal (String username) ; }
rebuild项目,postman下进行test
渲染到前端页面Home.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 <template> <el-container style="min-height: 100vh"> <el-aside :width="sideWidth + 'px'" style="box-shadow: 2px 0 6px rgb(0 21 41 / 35%);"> <el-menu :default-openeds="['1', '3']" style="min-height: 100%; overflow-x: hidden" background-color="rgb(48, 65, 86)" text-color="#fff" active-text-color="#ffd04b" :collapse-transition="false" :collapse="isCollapse" > <div style="height: 60px; line-height: 60px; text-align: center"> <img src="../assets/logo.png" alt="" style="width: 20px; position: relative; top: 5px; right: 5px"> <b style="color: white" v-show="logoTextShow">后台管理系统</b> </div> <el-submenu index="1"> <template slot="title"> <i class="el-icon-message"></i> <span slot="title">导航一</span> </template> <el-menu-item-group title="分组2"> <el-menu-item index="1-3">选项3</el-menu-item> </el-menu-item-group> <el-submenu index="1-4"> <template slot="title">选项4</template> <el-menu-item index="1-4-1">选项4-1</el-menu-item> </el-submenu> </el-submenu> <el-submenu index="2"> <template slot="title"> <i class="el-icon-menu"></i> <span slot="title">导航二</span> </template> <el-submenu index="2-4"> <template slot="title">选项4</template> <el-menu-item index="2-4-1">选项4-1</el-menu-item> </el-submenu> </el-submenu> <el-submenu index="3"> <template slot="title"> <i class="el-icon-setting"></i> <span slot="title">导航三</span> </template> <el-submenu index="3-4"> <template slot="title">选项4</template> <el-menu-item index="3-4-1">选项4-1</el-menu-item> </el-submenu> </el-submenu> </el-menu> </el-aside> <el-container> <el-header style="font-size: 12px; border-bottom: 1px solid #ccc; line-height: 60px; display: flex"> <div style="flex: 1; font-size: 20px"> <span :class="collapseBtnClass" style="cursor: pointer" @click="collapse"></span> </div> <el-dropdown style="width: 70px; cursor: pointer"> <span>王小虎</span><i class="el-icon-arrow-down" style="margin-left: 5px"></i> <el-dropdown-menu slot="dropdown"> <el-dropdown-item style="font-size: 14px; padding: 5px 0">个人信息</el-dropdown-item> <el-dropdown-item style="font-size: 14px; padding: 5px 0">退出</el-dropdown-item> </el-dropdown-menu> </el-dropdown> </el-header> <el-main> <div style="margin-bottom: 30px"> <el-breadcrumb separator="/"> <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item> <el-breadcrumb-item>用户管理</el-breadcrumb-item> </el-breadcrumb> </div> <div style="margin: 10px 0"> <el-input style="width: 200px" placeholder="请输入名称" suffix-icon="el-icon-search" v-model="username"></el-input> <!-- <el-input style="width: 200px" placeholder="请输入邮箱" suffix-icon="el-icon-message" class="ml-5"></el-input>--> <!-- <el-input style="width: 200px" placeholder="请输入地址" suffix-icon="el-icon-position" class="ml-5"></el-input>--> <el-button class="ml-5" type="primary" @click="load">搜索</el-button> </div> <div style="margin: 10px 0"> <el-button type="primary">新增 <i class="el-icon-circle-plus-outline"></i></el-button> <el-button type="danger">批量删除 <i class="el-icon-remove-outline"></i></el-button> <el-button type="primary">导入 <i class="el-icon-bottom"></i></el-button> <el-button type="primary">导出 <i class="el-icon-top"></i></el-button> </div> <el-table :data="tableData" border stripe :header-cell-class-name="headerBg"> <el-table-column prop="id" label="ID" width="80"> </el-table-column> <el-table-column prop="username" label="用户名" width="140"> </el-table-column> <el-table-column prop="nickname" label="昵称" width="120"> </el-table-column> <el-table-column prop="email" label="邮箱"> </el-table-column> <el-table-column prop="phone" label="电话"> </el-table-column> <el-table-column prop="address" label="地址"> </el-table-column> <el-table-column label="操作" width="200" align="center"> <template slot-scope="scope"> <el-button type="success">编辑 <i class="el-icon-edit"></i></el-button> <el-button type="danger">删除 <i class="el-icon-remove-outline"></i></el-button> </template> </el-table-column> </el-table> <div style="padding: 10px 0"> <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="pageNum" :page-sizes="[2, 5, 10, 20]" :page-size="pageSize" layout="total, sizes, prev, pager, next, jumper" :total="total"> </el-pagination> </div> </el-main> </el-container> </el-container> </template> <script> // @ is an alias to /src import HelloWorld from '@/components/HelloWorld.vue' export default { name: 'Home', data() { return { tableData: [], username: "", total: 0, pageNum: 1, pageSize: 2, msg: "hello sliu", collapseBtnClass: 'el-icon-s-fold', isCollapse: false, sideWidth: 200, logoTextShow: true, headerBg: 'headerBg' } }, created() { //请求分页查询数据 fetch( "http://localhost:9090/user/page?pageNum="+this.pageNum+"&pageSize="+this.pageSize+"&username="+this.username) .then(res => res.json()).then(res => { console.log(res) this.tableData=res.data this.total=res.total }) }, methods: { collapse() { // 点击收缩按钮触发 this.isCollapse = !this.isCollapse if (this.isCollapse) { // 收缩 this.sideWidth = 64 this.collapseBtnClass = 'el-icon-s-unfold' this.logoTextShow = false } else { // 展开 this.sideWidth = 200 this.collapseBtnClass = 'el-icon-s-fold' this.logoTextShow = true } }, load(){ fetch( "http://localhost:9090/user/page?pageNum="+this.pageNum+"&pageSize="+this.pageSize+"&username="+this.username) .then(res => res.json()).then(res => { console.log(res) this.tableData=res.data this.total=res.total }) }, handleSizeChange(pageSize){ console.log(pageSize) this.pageSize = pageSize this.load() }, handleCurrentChange(pageNum){ console.log(pageNum) this.pageNum = pageNum this.load() } } } </script> <style> .headerBg { background: #eee !important; } </style>
访问localhost:8080
SpringBoot集成Mybatis-plus
利用Mybatis-plus 实现分页查询
pom.xml下导入Mybatis-plus依赖
1 2 3 4 5 <dependency > <groupId > com.baomidou</groupId > <artifactId > mybatis-plus-boot-starter</artifactId > <version > 3.5.1</version > </dependency >
application.yaml下修改Mybatis-plus日志打印方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 server: port: 9090 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/sliu?serverTimezone=GMT%2b8 username: root password: 123456 mybatis: mapper-locations: classpath:mapper/*.xml mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
config文件夹下新建MybatisPlusConfig用于接管mapper注入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package com.sliu.springboot.config;import com.baomidou.mybatisplus.annotation.DbType;import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;import org.mybatis.spring.annotation.MapperScan;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configuration @MapperScan("com.sliu.springboot.mapper") public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor () { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor (); interceptor.addInnerInterceptor(new PaginationInnerInterceptor (DbType.MYSQL)); return interceptor; } }
删除UserMapper上@mapper注释
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 package com.sliu.springboot.mapper;import com.sliu.springboot.entity.User;import org.apache.ibatis.annotations.*;import java.util.List;public interface UserMapper { @Select("SELECT * from user") List<User> findAllUser () ; @Insert("Insert into user(username, password, nickname, email, phone, address) " + "VALUES(#{username}, #{password}, #{nickname}, #{email}, #{phone}, #{address})") int insertUser (User user) ; int updateUser (User user) ; @Delete("delete from user where id=#{id}") Integer deleteUserById (@Param("id") Integer id) ; @Select("SELECT * from user where username like concat('%', #{username}, '%') limit #{pageNum}, #{pageSize}") List<User> printUserByPage (Integer pageNum, Integer pageSize, String username) ; @Select("SELECT count(*) from user where username like concat('%', #{username}, '%')") Integer selectTotal (String username) ; }
rebuild项目,postman下进行test
使用mybatis_plus简化数据库crud
修改UserMapper继承BaseMapper
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 package com.sliu.springboot.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;import com.sliu.springboot.entity.User;import org.apache.ibatis.annotations.*;import java.util.List;public interface UserMapper extends BaseMapper <User> { @Select("SELECT * from user") List<User> findAllUser () ; @Insert("Insert into user(username, password, nickname, email, phone, address) " + "VALUES(#{username}, #{password}, #{nickname}, #{email}, #{phone}, #{address})") int insertUser (User user) ; int updateUser (User user) ; @Delete("delete from user where id=#{id}") Integer deleteUserById (@Param("id") Integer id) ; @Select("SELECT * from user where username like concat('%', #{username}, '%') limit #{pageNum}, #{pageSize}") List<User> printUserByPage (Integer pageNum, Integer pageSize, String username) ; @Select("SELECT count(*) from user where username like concat('%', #{username}, '%')") Integer selectTotal (String username) ; }
修改UserService
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package com.sliu.springboot.service;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;import com.sliu.springboot.entity.User;import com.sliu.springboot.mapper.UserMapper;import org.springframework.stereotype.Service;@Service public class UserService extends ServiceImpl <UserMapper, User> { public boolean saveUser (User user) { return saveOrUpdate(user); } }
修改User类指定表名和列名,同时在数据库user表中新增一个字段avatar_url用于测试指定数据库列名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 package com.sliu.springboot.entity;import com.baomidou.mybatisplus.annotation.TableField;import com.baomidou.mybatisplus.annotation.TableId;import com.baomidou.mybatisplus.annotation.TableName;import com.fasterxml.jackson.annotation.JsonIgnore;import javax.xml.crypto.Data;@lombok .Data@TableName(value = "user") public class User { @TableId(value = "id") private Integer id; private String username; @JsonIgnore private String password; private String nickname; private String email; private String phone; private String address; private Data createTime; @TableField(value = "avatar_url") private String avatar; }
rebuild项目,postman下进行测试
SpringBoot集成SwaggerUI
通过postman进行数据请求的测试比较麻烦,整合Swagger简化测试流程
pom.xml下导入依赖
1 2 3 4 5 6 <dependency > <groupId > io.springfox</groupId > <artifactId > springfox-boot-starter</artifactId > <version > 3.0.0</version > </dependency >
config文件夹下新建swaggerConfig配置类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 package com.sliu.springboot.config;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import springfox.documentation.builders.ApiInfoBuilder;import springfox.documentation.builders.PathSelectors;import springfox.documentation.builders.RequestHandlerSelectors;import springfox.documentation.oas.annotations.EnableOpenApi;import springfox.documentation.service.ApiInfo;import springfox.documentation.service.Contact;import springfox.documentation.spi.DocumentationType;import springfox.documentation.spring.web.plugins.Docket;import springfox.documentation.swagger2.annotations.EnableSwagger2;@Configuration @EnableOpenApi public class SwaggerConfig { @Bean public Docket docket () { return new Docket (DocumentationType.OAS_30) .apiInfo(apiInfo()).enable(true ) .select() .apis(RequestHandlerSelectors.basePackage("com.sliu.springboot.controller" )) .paths(PathSelectors.any()) .build(); } private ApiInfo apiInfo () { return new ApiInfoBuilder () .title("sliu的项目接口文档" ) .description("项目接口测试" ) .contact(new Contact ("sliu" , "https://shunliu.xyz" , "shunliu@csu.edu.cn" )) .version("1.0" ) .build(); } }
rebuild项目,访问localhost:9090/swagger-ui/index.html
继续改造UserMapper中数据操作,全部替换为mybatis-plus实现
修改UserMapper
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 package com.sliu.springboot.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;import com.sliu.springboot.entity.User;import org.apache.ibatis.annotations.*;import java.util.List;public interface UserMapper extends BaseMapper <User> {}
修改UserController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 package com.sliu.springboot.controller;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;import com.baomidou.mybatisplus.core.metadata.IPage;import com.baomidou.mybatisplus.extension.plugins.pagination.Page;import com.sliu.springboot.entity.User;import com.sliu.springboot.mapper.UserMapper;import com.sliu.springboot.service.UserService;import io.swagger.annotations.Api;import io.swagger.annotations.ApiOperation;import org.apache.logging.log4j.util.Strings;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.*;import java.util.HashMap;import java.util.List;import java.util.Map;@RestController @RequestMapping("/user") public class UserController { @Autowired UserService userService; @PostMapping public boolean save (@RequestBody User user) { return userService.saveUser(user); } @GetMapping public List<User> printAllUser () { return userService.list(); } @DeleteMapping("/{id}") public boolean deleteUserById (@PathVariable Integer id) { return userService.removeById(id); } @GetMapping("/page") public IPage<User> printUserByPage (@RequestParam Integer pageNum, @RequestParam Integer pageSize, @RequestParam(defaultValue = "") String username, @RequestParam(defaultValue = "") String nickname, @RequestParam(defaultValue = "") String address) { IPage<User> page = new Page <>(pageNum, pageSize); QueryWrapper<User> queryWrapper = new QueryWrapper <>(); queryWrapper.like(!Strings.isEmpty(username),"username" , username); queryWrapper.like(!Strings.isEmpty(nickname),"nickname" , nickname); queryWrapper.like(!Strings.isEmpty(address),"address" , address); return userService.page(page, queryWrapper); } }
rebuild项目,swagger下进行test
Coming soon......