Version: Next

10. 前后端分离——Vue结合BootStrap与SpringBoot

一个完整的前后端分离案例

  • 新建SpringBoot项目

  • 下载Vue、Bootstrap和axios

  • 删除resources目录下的statictemplates文件夹

  • 新建/src/main/webapp文件夹

  • 在项目中,引入Vue.js、axios.js,BootStrap的css和fonts

  • 导入fastjson

    <dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.45</version>
    </dependency>

image-20200509225543264

10.1 基本页面

users.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户管理界面</title>
<!-- 引入bootstrap-->
<link rel="stylesheet" href="./bootvue/css/bootstrap.css">
<link rel="stylesheet" href="./bootvue/css/bootstrap-theme.css">
</head>
<body>
<nav class="navbar navbar-default">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1"
aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">用户管理系统</a>
</div>
<ul class="nav navbar-nav navbar-right">
<li><a href="#">欢迎xx进入系统</a></li>
</ul>
</div><!-- /.navbar-collapse -->
</nav>
<!-- 中心布局-->
<div class="container-fluid">
<!-- 搜索-->
<div class="row">
<div class="col-md-8 col-md-offset-1">
<form class="form-inline">
<div class="form-group">
<label for="searchName">姓名</label>
<input type="text" class="form-control" id="searchName" placeholder="请输入姓名">
</div>
<div class="form-group">
<label for="searchId">ID</label>
<input type="text" class="form-control" id="searchId" placeholder="请输入id">
</div>
<button type="submit" class="btn btn-info">搜索</button>
</form>
</div>
</div>
<!-- 表格-->
<div class="row" style="margin-top: 20px">
<div class="col-md-8">
<!-- 用户信息展示-->
<table class="table table-hover table-striped">
<tr>
<th>ID</th>
<th>姓名</th>
<th>密码</th>
<th>操作</th>
</tr>
<tr>
<td>1</td>
<td>Alice</td>
<td>123456</td>
<td>
<button class="btn btn-primary btn-sm">修改</button>&nbsp;&nbsp;<button class="btn btn-danger btn-sm">删除
</button>
</td>
</tr>
</table>
</div>
<!--右侧-->
<div class="col-md-4">
<form>
<div class="form-group">
<label for="name">姓名</label>
<input type="text" class="form-control" id="name" placeholder="姓名">
</div>
<div class="form-group">
<label for="password">password</label>
<input type="text" class="form-control" id="password" placeholder="password">
</div>
<button type="button" class="btn btn-info">提交</button>
<button type="button" class="btn btn-danger">重置</button>
</form>
</div>
<!--右侧-->
</div>
</div>
<!-- 中心布局-->
<!--引入vue-->
<!--引入axios-->
<script src="./bootvue/js/vue.js"></script>
<script src="./bootvue/js/axios.min.js"></script>
</body>
</html>

image-20200510002935829

10.2 查询所有

将页面要展示的信息绑定到Vue中,将整个页面从导航到末尾用一个新div标签包裹起来

当页面一加载完,我们就希望前端向后端Controller请求数据,并把响应显示在页面上

users数据复制应该来源于后端服务接口,并再页面加载完之后users中包含数据

  • 生命周期created阶段——此时可访问到data中的数据
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户管理界面</title>
<!-- 引入bootstrap-->
<link rel="stylesheet" href="./bootvue/css/bootstrap.css">
<link rel="stylesheet" href="./bootvue/css/bootstrap-theme.css">
</head>
<body>
<div id="app">
<nav class="navbar navbar-default">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1"
aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">用户管理系统</a>
</div>
<ul class="nav navbar-nav navbar-right">
<li><a href="#">欢迎xx进入系统</a></li>
</ul>
</div><!-- /.navbar-collapse -->
</nav>
<!-- 中心布局-->
<div class="container-fluid">
<!-- 搜索-->
<div class="row">
<div class="col-md-8 col-md-offset-1">
<form class="form-inline">
<div class="form-group">
<label for="searchName">姓名</label>
<input type="text" class="form-control" id="searchName" placeholder="请输入姓名">
</div>
<div class="form-group">
<label for="searchId">ID</label>
<input type="text" class="form-control" id="searchId" placeholder="请输入id">
</div>
<button type="submit" class="btn btn-info">搜索</button>
</form>
</div>
</div>
<!-- 表格-->
<div class="row" style="margin-top: 20px">
<div class="col-md-8">
<!-- 用户信息展示-->
<table class="table table-hover table-striped">
<tr>
<th>ID</th>
<th>姓名</th>
<th>密码</th>
<th>操作</th>
</tr>
<tr v-for="u in users" :key="u.id">
<td>{ {u.id} }</td>
<td>{ {u.name} }</td>
<td>{ {u.password} }</td>
<td>
<button class="btn btn-primary btn-sm">修改</button>&nbsp;&nbsp;<button class="btn btn-danger btn-sm">删除
</button>
</td>
</tr>
</table>
</div>
<!--右侧-->
<div class="col-md-4">
<form>
<div class="form-group">
<label for="name">姓名</label>
<input type="text" class="form-control" id="name" placeholder="姓名">
</div>
<div class="form-group">
<label for="password">password</label>
<input type="text" class="form-control" id="password" placeholder="password">
</div>
<button type="button" class="btn btn-info">提交</button>
<button type="button" class="btn btn-danger">重置</button>
</form>
</div>
<!--右侧-->
</div>
</div>
<!-- 中心布局-->
</div>
<!--引入vue-->
<!--引入axios-->
<script src="./bootvue/js/vue.js"></script>
<script src="./bootvue/js/axios.min.js"></script>
<script>
const vue = new Vue({
el: "#app",
data: {
users: [] // 从后端请求然后填充
},
methods: {},
//生命周期函数created
created() {
//发送查询所有用户信息操作
axios.get("")
.then(function(response) {
console.log(response);
})
.catch(function(error) {
console.log(error);
})
}
})
</script>
</body>
</html>

新建数据库表

CREATE TABLE USER(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(30) DEFAULT NULL,
PASSWORD VARCHAR(20) DEFAULT NULL
)ENGINE=INNODB DEFAULT CHARSET=utf8;

SpringBoot整合Mybatis、Druid (具体看SpringBoot笔记)

  • pom.xml
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.21</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.2</version>
</dependency>
  • application.yaml
spring:
datasource:
username: root
password: root
#?serverTimezone=UTC解决时区的报错
url: jdbc:mysql://localhost:3306/test?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
driver-class-name: com.mysql.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource # 配置使用Druid数据源
#Spring Boot 默认是不注入这些属性值的,需要自己绑定
#druid 数据源专有配置
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
#配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入
#如果允许时报错 java.lang.ClassNotFoundException: org.apache.log4j.Priority
#则导入 log4j 依赖即可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4j
filters: stat,wall,log4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
@Accessors(chain = true)
public class User {
private Integer id;
private String name;
private String password;
}

mapper & mapper.xml

@Mapper
@Repository
public interface UserMapper {
//查询所有
List<User> findAll();
}
<?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.example.demo.dao.UserMapper">
<select id="findAll" resultType="com.example.demo.pojo.User">
SELECT * FROM USER;
</select>
</mapper>

Service & ServiceImpl

public interface UserService {
//查询所有用户
List<User> findAll();
}
@Service
@Transactional //事务
public class UserServiceImpl implements UserService{
@Autowired
private UserMapper userMapper;
@Override
@Transactional(propagation = Propagation.SUPPORTS)
public List<User> findAll() {
return userMapper.findAll();
}
}

Controller

  • 没有模板引擎的情况下,纯HTML访问后端接口,需要开启跨域
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/findAll")
@CrossOrigin // 跨域
public List<User> findAll(){
List<User> users = userService.findAll();
return users;
}
}

前端页面:

  • 修改axios部分代码,将异步请求路径设为后端定义的URL
  • 获取后端响应,将响应的data域赋值到Vue的data
<script>
const vue = new Vue({
el: "#app",
data: {
users: [] // 从后端请求然后填充
},
methods: {},
//生命周期函数created
created() {
//发送查询所有用户信息操作
axios.get("http://localhost:8080/user/findAll")
.then(function(response) {
vue.users = response.data;
})
.catch(function(error) {
console.log(error);
})
}
})
</script>

image-20200513054555883

10.3 添加

前端:

  1. 为提交按钮绑定单击事件saveUserInfo
  2. 获取表单数据
    • data域单独定义一个user对象
    • v-model来完成数据双向绑定
      • v-model="user.id"
      • v-model="user.name"
      • v-model="user.password"
    • saveUserInfo方法中发送axios.post请求到后端服务器,传递data中的user对象
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户管理界面</title>
<!-- 引入bootstrap-->
<link rel="stylesheet" href="./bootvue/css/bootstrap.css">
<link rel="stylesheet" href="./bootvue/css/bootstrap-theme.css">
</head>
<body>
<div id="app">
<nav class="navbar navbar-default">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1"
aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">用户管理系统</a>
</div>
<ul class="nav navbar-nav navbar-right">
<li><a href="#">欢迎xx进入系统</a></li>
</ul>
</div><!-- /.navbar-collapse -->
</nav>
<!-- 中心布局-->
<div class="container-fluid">
<!-- 搜索-->
<div class="row">
<div class="col-md-8 col-md-offset-1">
<form class="form-inline">
<div class="form-group">
<label for="searchName">姓名</label>
<input type="text" class="form-control" id="searchName" placeholder="请输入姓名">
</div>
<div class="form-group">
<label for="searchId">ID</label>
<input type="text" class="form-control" id="searchId" placeholder="请输入id">
</div>
<button type="submit" class="btn btn-info">搜索</button>
</form>
</div>
</div>
<!-- 表格-->
<div class="row" style="margin-top: 20px">
<div class="col-md-8">
<!-- 用户信息展示-->
<table class="table table-hover table-striped">
<tr>
<th>ID</th>
<th>姓名</th>
<th>密码</th>
<th>操作</th>
</tr>
<tr v-for="u in users" :key="u.id">
<td>{ {u.id} }</td>
<td>{ {u.name} }</td>
<td>{ {u.password} }</td>
<td>
<button class="btn btn-primary btn-sm">修改</button>&nbsp;&nbsp;<button class="btn btn-danger btn-sm">删除
</button>
</td>
</tr>
</table>
</div>
<!--右侧-->
<div class="col-md-4">
<form>
<div class="form-group">
<label for="name">姓名</label>
<input type="text" class="form-control" id="name" placeholder="姓名" v-model="user.name">
</div>
<div class="form-group">
<label for="password">password</label>
<input type="text" class="form-control" id="password" placeholder="密码" v-model="user.password">
</div>
<button type="button" class="btn btn-info" @click="saveUserInfo">提交</button>
<button type="button" class="btn btn-danger">重置</button>
</form>
</div>
<!--右侧-->
</div>
</div>
<!-- 中心布局-->
</div>
<!--引入vue-->
<!--引入axios-->
<script src="./bootvue/js/vue.js"></script>
<script src="./bootvue/js/axios.min.js"></script>
<script>
const vue = new Vue({
el: "#app",
data: {
users: [], // 从后端请求然后填充
user: {}
},
methods: {
saveUserInfo() {
axios.post("http://localhost:8080/user/save", this.user)
.then(function(response) {
console.log(response.data);
})
.catch(function(error) {
console.log(error);
});
}
},
//生命周期函数created
created() {
//发送查询所有用户信息操作
axios.get("http://localhost:8080/user/findAll")
.then(function(response) {
vue.users = response.data;
})
.catch(function(error) {
console.log(error);
})
}
})
</script>
</body>
</html>

后端:

  • mapper定义保存用户抽象方法
  • mapper.xml定义sql语句
  • service层
  • 添加/save路由,接受user对象,插入数据库
  • 向前端响应一个Map,包含success键包含boolean类型值说明添加成功或失败;message键包含说明信息
  • mapper
@Mapper
@Repository
public interface UserMapper {
//查询所有
List<User> findAll();
int save(User user);
}
  • mapper.xml
<insert id="save" parameterType="com.example.demo.pojo.User">
insert into user (id, name, password) values (null, #{name}, #{password})
</insert>
  • Service
public interface UserService {
//查询所有用户
List<User> findAll();
int save(User user);
}
@Service
@Transactional //事务
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
@Transactional(propagation = Propagation.SUPPORTS)
public List<User> findAll() {
return userMapper.findAll();
}
@Override
@Transactional(propagation = Propagation.SUPPORTS)
public int save(User user) {
return userMapper.save(user);
}
}
  • Controller
@PostMapping("/save")
@CrossOrigin
public Map<String, Object> save(@RequestBody User user) {
Map<String, Object> map = new HashMap<>();
try {
userService.save(user);
map.put("success", true);
map.put("message","用户保存成功");
} catch (Exception e) {
map.put("success", false);
map.put("message","用户保存失败");
}
return map;
}

前端:

  • 查询所有的代码封装为独立方法findAll
  • 根据后端响应的scuccess键,决定是否调用findAll方法刷新页面列表
<script>
const vue = new Vue({
el: "#app",
data: {
users: [], // 从后端请求然后填充
user: {}
},
methods: {
saveUserInfo() {
axios.post("http://localhost:8080/user/save", this.user)
.then(function(response) {
if (response.data.success) {
this.findAll();
} else {
alert(response.data.message);
}
})
.catch(function(error) {
console.log(error);
});
},
findAll() {
axios.get("http://localhost:8080/user/findAll")
.then(function(response) {
vue.users = response.data;
})
.catch(function(error) {
console.log(error);
})
}
},
//生命周期函数created
created() {
//发送查询所有用户信息操作
this.findAll();
}
})
</script>

10.4 删除

10.4.1 表单重置按钮

  • 重置按钮上添加单击事件
  • 在事件中将data域中的user对象置为{}
<button type="button" class="btn btn-danger" @click="reset">重置</button>
reset() {
this.user = {};
}

10.4.2 删除用户

前端

  • 单击删除按钮,绑定单击事件,事件触发时传递id
  • 在事件中向后端发送delete请求,传递用户id
  • 接收后端响应success,以此决定是否执行findAll刷新页面列表
//省略添加单击事件的代码
const vue = new Vue({
el: "#app",
data: {
users: [], // 从后端请求然后填充
user: {}
},
methods: {
saveUserInfo() {
axios.post("http://localhost:8080/user/save", this.user)
.then(function(response) {
if (response.data.success) {
vue.findAll();
} else {
alert(response.data.message);
}
})
.catch(function(error) {
console.log(error);
});
},
findAll() {
axios.get("http://localhost:8080/user/findAll")
.then(function(response) {
vue.users = response.data;
})
.catch(function(error) {
console.log(error);
})
},
reset() {
this.user = {};
},
deleteUser(id) {
axios.delete("http://localhost:8080/user/delete/" + id)
.then(function(response) {
if (response.data.success) {
vue.findAll();
} else {
alert(response.data.message);
}
});
}
},
//生命周期函数created
created() {
//发送查询所有用户信息操作
this.findAll();
}
})

后端

  • 接受前端发送的id
  • 根据id删除数据库对应记录
  • 向前端响应结果信息success
  • Mapper
@Mapper
@Repository
public interface UserMapper {
//查询所有
List<User> findAll();
int save(User user);
void delete(Integer id);
}
  • mapper.xml
<delete id="delete">
delete from user where id = #{id}
</delete>
  • service
public interface UserService {
//查询所有用户
List<User> findAll();
int save(User user);
void delete(Integer id);
}
@Service
@Transactional //事务
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
@Transactional(propagation = Propagation.SUPPORTS)
public List<User> findAll() {
return userMapper.findAll();
}
@Override
@Transactional(propagation = Propagation.SUPPORTS)
public int save(User user) {
return userMapper.save(user);
}
@Override
@Transactional(propagation = Propagation.SUPPORTS)
public void delete(Integer id) {
userMapper.delete(id);
}
}
  • controller
@DeleteMapping("/delete/{id}")
public Map<String, Object> delete(@PathVariable("id") Integer id) {
Map<String, Object> map = new HashMap<>();
try {
userService.delete(id);
map.put("success", true);
} catch (Exception e) {
map.put("success", false);
map.put("message", "删除失败");
}
return null;
}

10.5 修改

  • 当单击修改按钮时,将对应用户信息显示在页面右侧输入框中
  • 提交行为根据表单中有没有id属性来决定具体行为
    • id:单击提交进行修改
    • id:单击提交进行添加
<button class="btn btn-primary btn-sm" @click="findOneUser(u)">修改</button>
const vue = new Vue({
el: "#app",
data: {
users: [], // 从后端请求然后填充
user: {}
},
methods: {
saveUserInfo() {
axios.post("http://localhost:8080/user/save", this.user)
.then(function(response) {
if (response.data.success) {
vue.findAll();
vue.reset();
} else {
alert(response.data.message);
}
})
.catch(function(error) {
console.log(error);
});
},
findAll() {
axios.get("http://localhost:8080/user/findAll")
.then(function(response) {
vue.users = response.data;
})
.catch(function(error) {
console.log(error);
})
},
reset() {
this.user = {};
},
deleteUser(id) {
axios.delete("http://localhost:8080/user/delete/" + id)
.then(function(response) {
if (response.data.success) {
vue.findAll();
} else {
alert(response.data.message);
}
});
},
findOneUser(u) {
this.user = u;
}
},
//生命周期函数created
created() {
//发送查询所有用户信息操作
this.findAll();
}
})

后端:

  • 定义根据id修改用户信息的方法
  • mapper
@Mapper
@Repository
public interface UserMapper {
//查询所有
List<User> findAll();
//修改一个人
void update(User user);
int save(User user);
void delete(Integer id);
}
  • mapper.xml
<update id="update" parameterType="com.example.demo.pojo.User">
update user set name = #{name}, password = #{password} where id = #{id}
</update>
  • service
public interface UserService {
//查询所有用户
List<User> findAll();
int save(User user);
void delete(Integer id);
void update(User user);
}
  • ServiceImpl
@Override
@Transactional(propagation = Propagation.SUPPORTS)
public void update(User user) {
userMapper.update(user);
}
  • controller 修改/save路由,判断传进来的user对象是否有id属性
    • id:修改
    • id:添加
@PostMapping("/save")
public Map<String, Object> save(@RequestBody User user) {
Map<String, Object> map = new HashMap<>();
try {
if (null != user.getId()) {
userService.update(user);
} else {
userService.save(user);
}
map.put("success", true);
} catch (Exception e) {
map.put("success", false);
map.put("message", "操作失败");
}
return map;
}

10.6 搜索

根据姓名或者id进行模糊搜索

后端

  • 使用动态SQL实现模糊查询
  • 响应查询用户列表给前端
  • mapper
//根据姓名或者id进行模糊搜索
List<User> searchByNameOrId(@Param("name") String name,@Param("id") Integer id);
  • mapper.xml 在mybatis中模糊查询使用concat表达式
<!--模糊搜索-->
<select id="searchByNameOrId" resultType="com.example.demo.pojo.User">
SELECT * FROM USER
<where>
<if test="name!='' and name!=null">
NAME LIKE concat('%', #{name}, '%')
</if>
<if test="id!='' and id!=null">
OR id LIKE concat('%', #{id}, '%')
</if>
</where>
</select>
  • service
//根据姓名或者id进行模糊搜索
List<User> searchByNameOrId(String name, Integer id);
  • serviceImpl
@Override
@Transactional(propagation = Propagation.SUPPORTS)
public List<User> searchByNameOrId(String name, Integer id) {
return userMapper.searchByNameOrId(name, id);
}
  • controller
    • 利用fastJsonJSONObject类接收json类型的请求参数
//模糊搜索 利用fastJson接收json格式的参数
@PostMapping("/search")
public List<User> searchByNameOrId(@RequestBody JSONObject jsonParams) {
String name = jsonParams.get("name") + "";
Integer id;
if (null == jsonParams.get("id") || "".equals(jsonParams.get("id"))) {
id = null;
} else {
id = Integer.parseInt(jsonParams.get("id").toString());
}
return userService.searchByNameOrId(name, id);
}

前端

  • 搜索按钮绑定单击事件,使用对象searchObj封装nameid
  • POST方式提交nameid值到/search路由
  • 将后端响应的用户列表进行显示
  • 搜索组件与单击事件
<!-- 搜索-->
<div class="row">
<div class="col-md-8 col-md-offset-1">
<form class="form-inline">
<div class="form-group">
<label for="searchName">姓名</label>
<input type="text" v-model="searchObj.name" class="form-control" id="searchName" placeholder="请输入姓名">
</div>
<div class="form-group">
<label for="searchId">ID</label>
<input type="text" v-model="searchObj.id" class="form-control" id="searchId" placeholder="请输入id">
</div>
<button type="button" class="btn btn-info" @click="search">搜索</button>
</form>
</div>
</div>
  • 事件函数
search() { //搜索方法
axios.post("http://localhost:8080/user/search", this.searchObj)
.then(function(response) {
vue.users = response.data;
})
.catch(function(error) {
console.log(error);
})
}
  • data域添加searchObj
data: {
users: [], // 从后端请求然后填充
user: {},
searchObj: {} // 搜索实体
},

10.7 完整代码

前端

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户管理界面</title>
<!-- 引入bootstrap-->
<link rel="stylesheet" href="./bootvue/css/bootstrap.css">
<link rel="stylesheet" href="./bootvue/css/bootstrap-theme.css">
</head>
<body>
<div id="app">
<nav class="navbar navbar-default">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1"
aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">用户管理系统</a>
</div>
<ul class="nav navbar-nav navbar-right">
<li><a href="#">欢迎xx进入系统</a></li>
</ul>
</div><!-- /.navbar-collapse -->
</nav>
<!-- 中心布局-->
<div class="container-fluid">
<!-- 搜索-->
<div class="row">
<div class="col-md-8 col-md-offset-1">
<form class="form-inline">
<div class="form-group">
<label for="searchName">姓名</label>
<input type="text" v-model="searchObj.name" class="form-control" id="searchName" placeholder="请输入姓名">
</div>
<div class="form-group">
<label for="searchId">ID</label>
<input type="text" v-model="searchObj.id" class="form-control" id="searchId" placeholder="请输入id">
</div>
<button type="button" class="btn btn-info" @click="search">搜索</button>
</form>
</div>
</div>
<!-- 表格-->
<div class="row" style="margin-top: 20px">
<div class="col-md-8">
<!-- 用户信息展示-->
<table class="table table-hover table-striped">
<tr>
<th>ID</th>
<th>姓名</th>
<th>密码</th>
<th>操作</th>
</tr>
<tr v-for="u in users" :key="u.id">
<td>{ {u.id} }</td>
<td>{ {u.name} }</td>
<td>{ {u.password} }</td>
<td>
<button class="btn btn-primary btn-sm" @click="findOneUser(u)">修改</button>&nbsp;&nbsp;<button class="btn btn-danger btn-sm"
@click="deleteUser(u.id)">删除
</button>
</td>
</tr>
</table>
</div>
<!--右侧-->
<div class="col-md-4">
<form>
<div class="form-group">
<label for="name">姓名</label>
<input type="text" class="form-control" id="name" placeholder="姓名" v-model="user.name">
</div>
<div class="form-group">
<label for="password">password</label>
<input type="text" class="form-control" id="password" placeholder="密码" v-model="user.password">
</div>
<button type="button" class="btn btn-info" @click="saveUserInfo">提交</button>
<button type="button" class="btn btn-danger" @click="reset">重置</button>
</form>
</div>
<!--右侧-->
</div>
</div>
<!-- 中心布局-->
</div>
<!--引入vue-->
<!--引入axios-->
<script src="./bootvue/js/vue.js"></script>
<script src="./bootvue/js/axios.min.js"></script>
<script>
const vue = new Vue({
el: "#app",
data: {
users: [], // 从后端请求然后填充
user: {},
searchObj: {} // 搜索实体
},
methods: {
saveUserInfo() {
axios.post("http://localhost:8080/user/save", this.user)
.then(function(response) {
if (response.data.success) {
vue.findAll();
vue.reset();
} else {
alert(response.data.message);
}
})
.catch(function(error) {
console.log(error);
});
},
findAll() {
axios.get("http://localhost:8080/user/findAll")
.then(function(response) {
vue.users = response.data;
})
.catch(function(error) {
console.log(error);
})
},
reset() {
this.user = {};
},
deleteUser(id) {
axios.delete("http://localhost:8080/user/delete/" + id)
.then(function(response) {
if (response.data.success) {
vue.findAll();
} else {
alert(response.data.message);
}
});
},
findOneUser(u) {
this.user = u;
},
search() { //搜索方法
axios.post("http://localhost:8080/user/search", this.searchObj)
.then(function(response) {
vue.users = response.data;
})
.catch(function(error) {
console.log(error);
})
}
},
//生命周期函数created
created() {
//发送查询所有用户信息操作
this.findAll();
}
})
</script>
</body>
</html>

后端

  • pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.7.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.2</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.21</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.45</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
  • application.yaml
spring:
datasource:
username: root
password: root
#?serverTimezone=UTC解决时区的报错
url: jdbc:mysql://localhost:3306/test?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
driver-class-name: com.mysql.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource # 配置使用Druid数据源
#Spring Boot 默认是不注入这些属性值的,需要自己绑定
#druid 数据源专有配置
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
#配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入
#如果允许时报错 java.lang.ClassNotFoundException: org.apache.log4j.Priority
#则导入 log4j 依赖即可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4j
filters: stat,wall,log4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
  • mapper
@Mapper
@Repository
public interface UserMapper {
//查询所有
List<User> findAll();
//修改一个人
void update(User user);
int save(User user);
void delete(Integer id);
//根据姓名或者id进行模糊搜索
List<User> searchByNameOrId(@Param("name") String name,@Param("id") Integer id);
}
  • mapper.xml
<?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.example.demo.dao.UserMapper">
<select id="findAll" resultType="com.example.demo.pojo.User">
SELECT * FROM USER;
</select>
<insert id="save" parameterType="com.example.demo.pojo.User">
insert into user (id, name, password) values (null, #{name}, #{password})
</insert>
<delete id="delete">
delete from user where id = #{id}
</delete>
<update id="update" parameterType="com.example.demo.pojo.User">
update user set name = #{name}, password = #{password} where id = #{id}
</update>
<!--模糊搜索-->
<select id="searchByNameOrId" resultType="com.example.demo.pojo.User">
SELECT * FROM USER
<where>
<if test="name!='' and name!=null">
NAME LIKE concat('%', #{name}, '%')
</if>
<if test="id!='' and id!=null">
OR id LIKE concat('%', #{id}, '%')
</if>
</where>
</select>
</mapper>
  • service
public interface UserService {
//查询所有用户
List<User> findAll();
int save(User user);
void delete(Integer id);
void update(User user);
//根据姓名或者id进行模糊搜索
List<User> searchByNameOrId(String name, Integer id);
}
  • serviceImpl
@Service
@Transactional //事务
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
@Transactional(propagation = Propagation.SUPPORTS)
public List<User> findAll() {
return userMapper.findAll();
}
@Override
@Transactional(propagation = Propagation.SUPPORTS)
public int save(User user) {
return userMapper.save(user);
}
@Override
@Transactional(propagation = Propagation.SUPPORTS)
public void delete(Integer id) {
userMapper.delete(id);
}
@Override
@Transactional(propagation = Propagation.SUPPORTS)
public void update(User user) {
userMapper.update(user);
}
@Override
@Transactional(propagation = Propagation.SUPPORTS)
public List<User> searchByNameOrId(String name, Integer id) {
return userMapper.searchByNameOrId(name, id);
}
}
  • Controller
@CrossOrigin
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/findAll")
public List<User> findAll() {
List<User> users = userService.findAll();
return users;
}
@PostMapping("/save")
public Map<String, Object> save(@RequestBody User user) {
Map<String, Object> map = new HashMap<>();
try {
if (null != user.getId()) {
userService.update(user);
} else {
userService.save(user);
}
map.put("success", true);
} catch (Exception e) {
map.put("success", false);
map.put("message", "操作失败");
}
return map;
}
@DeleteMapping("/delete/{id}")
public Map<String, Object> delete(@PathVariable("id") Integer id) {
Map<String, Object> map = new HashMap<>();
try {
userService.delete(id);
map.put("success", true);
} catch (Exception e) {
map.put("success", false);
map.put("message", "删除失败");
}
return map;
}
//模糊搜索 利用fastJson接收json格式的参数
@PostMapping("/search")
public List<User> searchByNameOrId(@RequestBody JSONObject jsonParams) {
String name = jsonParams.get("name") + "";
Integer id;
if (null == jsonParams.get("id") || "".equals(jsonParams.get("id"))) {
id = null;
} else {
id = Integer.parseInt(jsonParams.get("id").toString());
}
return userService.searchByNameOrId(name, id);
}
}