项目部分结构如下
AdminController 用户管理的控制器 访问地址 http://localhost:8080/admins
package com.waylau.spring.boot.blog.controller;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import com.waylau.spring.boot.blog.vo.Menu;
/**
* 后台管理 控制器
* @author Administrator
*/
@Controller
@RequestMapping("/admins")
public class AdminController {
/**
* 获取后台管理主页面
* @return
*/
@GetMapping
public ModelAndView listUsers(Model model) {
List<Menu> list = new ArrayList<Menu>();
list.add(new Menu("用户管理", "/users"));
model.addAttribute("list", list);
return new ModelAndView("admins/index", "model", model);
}
}
显示页面如下 此时没有用户的数据
其中Menu是菜单 目前只有一个【用户管理】菜单,页面位于/src/main/resources/templates/admins/index.html
前端页面为index.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<head th:replace="~{fragments/header :: header}">
</head>
<body>
<!-- Page Content -->
<div class="container blog-content-container">
<div class="row">
<!-- 左侧栏目 -->
<div class="col-md-4 col-xl-3">
<!-- 分类 -->
<div class="card ">
<h5 class="card-header"><i class="fa fa-bars" aria-hidden="true"></i> 菜单</h5>
<ul style="list-style-type:none;" class="list-group blog-menu" th:each="menu : ${model.list}">
<li>
<a href="javascript:void(0)" class="list-group-item active" title="waylau"
th:title="${menu.name}" th:text="${menu.name}" th:attr="url=${menu.url}">
用户管理xxx
</a>
</li>
</ul>
</div>
</div>
<!-- 右侧栏目(通过点击li里面的超链接异步加载) -->
<div class="col-md-8 col-xl-9">
<div class="card" id="rightContainer">
</div>
</div>
</div>
<!-- /.row -->
</div>
<!-- /.container -->
<div th:replace="~{fragments/footer :: footer}">...</div>
<script src="../../js/admins/main.js" th:src="@{~/js/admins/main.js}"></script>
</body>
</html>
该页面中引入了footer和header两个html页面
footer.html 主要是引入一些js 和页面下方说明 以及锚点的设置及滚动
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
</head>
<body>
<footer class="blog-footer bg-inverse" data-th-fragment="footer">
<a id="goToTop" href="#">
<i class="fa fa-chevron-up fa-3x" aria-hidden="true"></i>
</a>
<div class="container">
<p class="m-0 text-center text-white">© 2020 <a href="https://waylau.com">waylau.com</a></p>
</div>
<!-- JavaScript -->
<script src="../../js/jquery-3.1.1.min.js" th:src="@{/js/jquery-3.1.1.min.js}"></script>
<script src="../../js/jquery.form.min.js" th:src="@{/js/jquery.form.min.js}"></script>
<script src="../../js/tether.min.js" th:src="@{/js/tether.min.js}"></script>
<script src="../../js/bootstrap.min.js" th:src="@{/js/bootstrap.min.js}"></script>
<script src="../../js/nprogress.js" th:src="@{/js/nprogress.js}"></script>
<script src="../../js/thinker-md.vendor.min.js" th:src="@{/js/thinker-md.vendor.min.js}"></script>
<script src="../../js/bootstrap-tagsinput.js" th:src="@{/js/jquery.tagsinput.min.js}"></script>
<script src="../../js/chosen.jquery.js" th:src="@{/js/chosen.jquery.js}"></script>
<script src="../../js/toastr.min.js" th:src="@{/js/toastr.min.js}"></script>
<script src="../../js/cropbox.js" th:src="@{/js/cropbox.js}"></script>
<script src="../../js/thymeleaf-bootstrap-paginator.js" th:src="@{/js/thymeleaf-bootstrap-paginator.js}"></script>
<script src="../../js/catalog-generator.js" th:src="@{/js/catalog-generator.js}"></script>
<script src="../../js/main.js" th:src="@{/js/main.js}"></script>
</footer>
</body>
</html>
js/main.js 如下:
// DOM 加载完再执行
$(function() {
// 返回顶部的效果事件
NProgress.start();
$(window).scroll(function(){ //只要窗口滚动,就触发下面代码
var scrollt = document.documentElement.scrollTop + document.body.scrollTop; //获取滚动后的高度
if( scrollt >200 ){ //判断滚动后高度超过200px,就显示
$("#goToTop").fadeIn(400); //淡出
}else{
$("#goToTop").stop().fadeOut(400); //如果返回或者没有超过,就淡入.必须加上stop()停止之前动画,否则会出现闪动
}
});
$("#goToTop").click(function(){ //当点击标签的时候,使用animate在200毫秒的时间内,滚到顶部
$("html,body").animate({scrollTop:"0px"},200);
});
NProgress.done();
});
header.html 引入css 包括 搜索 登录 注册
<!DOCTYPE html>
<html lang="en" data-th-fragment="header" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Tether core CSS -->
<link href="../../css/tether.min.css" th:href="@{/css/tether.min.css}" rel="stylesheet">
<!-- Bootstrap CSS -->
<link href="../../css/bootstrap.min.css" th:href="@{/css/bootstrap.min.css}" rel="stylesheet">
<!-- Font-Awesome CSS -->
<link href="../../css/font-awesome.min.css" th:href="@{/css/font-awesome.min.css}" rel="stylesheet">
<!-- NProgress CSS -->
<link href="../../css/nprogress.css" th:href="@{/css/nprogress.css}" rel="stylesheet">
<!-- thinker-md CSS -->
<link href="../../css/thinker-md.vendor.css" th:href="@{/css/thinker-md.vendor.css}" rel="stylesheet">
<!-- bootstrap tags CSS -->
<link href="../../css/bootstrap-tagsinput.css" th:href="@{/css/jquery.tagsinput.min.css}" rel="stylesheet">
<!-- bootstrap chosen CSS -->
<link href="../../css/component-chosen.min.css" th:href="@{/css/component-chosen.min.css}" rel="stylesheet">
<!-- toastr CSS -->
<link href="../../css/toastr.min.css" th:href="@{/css/toastr.min.css}" rel="stylesheet">
<!-- jQuery image cropping plugin CSS -->
<link href="../../css/cropbox.css" th:href="@{/css/cropbox.css}" rel="stylesheet">
<!-- Custom styles -->
<link href="../../css/style.css" th:href="@{/css/style.css}" rel="stylesheet">
<link href="../../css/thymeleaf-bootstrap-paginator.css" th:href="@{/css/thymeleaf-bootstrap-paginator.css}" rel="stylesheet">
<link href="../../css/blog.css" th:href="@{/css/blog.css}" rel="stylesheet">
</head>
<body>
<nav class="navbar navbar-inverse bg-inverse navbar-toggleable-md">
<div class="container">
<button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse"
data-target="#navbarsContainer" aria-controls="navbarsExampleContainer" aria-expanded="false"
aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<a class="navbar-brand" href="/" th:href="@{/}">NewStarBlog</a>
<div class="collapse navbar-collapse" id="navbarsContainer">
<ul class="navbar-nav mr-auto">
<li class="nav-item">
<a class="nav-link" href="/blogs?order=new">最新 <span class="sr-only">(current)</span></a>
</li>
<li class="nav-item">
<a class="nav-link" href="/blogs?order=hot">最热</a>
</li>
<li>
<form class="form-inline mt-2 mt-md-0">
<input class="form-control mr-sm-2" type="text" placeholder="搜索">
<a href="/blogs?keyword=ww" class="btn btn-outline-secondary my-2 my-sm-0"><i class="fa fa-search" aria-hidden="true"></i></a>
</form>
</li>
</ul>
<a href="/login" class="btn btn-outline-success my-2 my-sm-0" type="submit">登录</a>
<a href="/register" class="btn btn-outline-success my-2 my-sm-0" type="submit">注册</a>
</div>
</div>
</nav>
</body>
</html>
在index.html下方引入了一个js
<script src="../../js/admins/main.js" th:src="@{~/js/admins/main.js}"></script>
代码如下
/**
* Bolg main JS.
* Created by waylau.com on 2017/3/9.
*/
"use strict";
//# sourceURL=main.js
// DOM 加载完再执行
$(function() {
// 菜单事件
$(".blog-menu .list-group-item").click(function() {//超链接a的点击事件
var url = $(this).attr("url");
// 先移除其他的点击样式,再添加当前的点击样式
$(".blog-menu .list-group-item").removeClass("active");
$(this).addClass("active");
// 加载其他模块的页面到右侧工作区
$.ajax({
url: url,
success: function(data){
$("#rightContainer").html(data);
},
error : function() {
alert("error");
}
});
});
// 选中菜单第一项(页面加载完毕后默认触发第一个li的点击事件)
$(".blog-menu .list-group-item:first").trigger("click");
});
该js对左侧菜单下的超链接绑定单击事件,点击后会给该链接添加背景,其余菜单取消背景 【class=active】
发动ajax请求,加载右侧数据区域 /users 默认异步 GET
进入UserController 控制器 调用list方法 此时后台无任何参数传递
package com.waylau.spring.boot.blog.controller;
import java.util.List;
import javax.validation.ConstraintViolationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.http.ResponseEntity;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
import com.waylau.spring.boot.blog.domain.User;
import com.waylau.spring.boot.blog.service.UserService;
import com.waylau.spring.boot.blog.util.ConstraintViolationExceptionHandler;
import com.waylau.spring.boot.blog.vo.Response;
/**
* User 控制器
* @author Administrator
*
*/
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private UserService userService;
/**
* 查询所有用户
* @param async
* @param pageIndex
* @param pageSize
* @param name
* @param model
* @return
*/
@GetMapping
public ModelAndView list(@RequestParam(value = "async",required = false) boolean async,//是否异步请求
@RequestParam(value = "pageIndex",required = false,defaultValue = "0") int pageIndex,//分页使用
@RequestParam(value = "pageSize",required = false,defaultValue = "10") int pageSize,//分页使用
@RequestParam(value = "name",required = false,defaultValue = "") String name,//模糊查询使用
Model model){
Pageable pageable = new PageRequest(pageIndex, pageSize);
Page<User> page = userService.listUsersByNameLike(name, pageable);
List<User> list = page.getContent();//当前所在页面数据列表
model.addAttribute("page", page);
model.addAttribute("userList", list);
//String viewName, String modelName, Object modelObject
/**
* 若为异步 则返回该list页面的一部分 若是同步则返回整个list页面
* 例如 第一次访问的时候 需要返回整个list页面
* 后面加载该list页面的时候只需要更新数据区域即可
*/
return new ModelAndView(async==true?"users/list :: #mainContainerRepleace":"users/list", "userModel", model);
}
/**
* 获取创建用户表单界面
* @param model
* @return
*/
@GetMapping("/add")
public ModelAndView createForm(Model model){
model.addAttribute("user", new User(null,null,null,null));
return new ModelAndView("users/add", "userModel", model);
}
/**
* 保存或修改用户
* @param user
* @return
*/
@PostMapping
public ResponseEntity<Response> saveOrUpdateUser(User user){
try{
userService.saveOrUpdateUser(user);
} catch (ConstraintViolationException e) {
return ResponseEntity.ok().body(new Response(false, ConstraintViolationExceptionHandler.getMessage(e)));
}
return ResponseEntity.ok().body(new Response(true, "处理成功", user));
}
/**
* 删除用户
* @param id
* @param model
* @return
*/
@DeleteMapping(value = "/{id}")
public ResponseEntity<Response> delete(@PathVariable("id") Long id, Model model){
try{
userService.removeUser(id);
} catch (Exception e) {
return ResponseEntity.ok().body(new Response(false, e.getMessage()));
}
return ResponseEntity.ok().body(new Response(true, "处理成功"));
}
/**
* 获取修改用户的界面
* @param id
* @param model
* @return
*/
@GetMapping(value = "/edit/{id}")
public ModelAndView modifyForm(@PathVariable("id") Long id, Model model) {
User user = userService.getUserById(id);
model.addAttribute("user", user);
return new ModelAndView("users/edit", "userModel", model);
}
}
users/list 代码如下
<div class="card-header bg-dark font-white">
<div class="input-group col-md-7 col-xl-6">
<input type="text" class="form-control" id="searchName" placeholder="输入用户名称进行搜索"/>
<span class="input-group-btn">
<button class="btn btn-secondary" type="button" id="searchNameBtn">
<i class="fa fa-search" aria-hidden="true"></i>
</button>
</span>
<a class="btn btn-primary" data-toggle="modal" data-target="#flipFlop" role="button" id="addUser">
<i class="fa fa-plus" aria-hidden="true"></i>
</a>
</div>
</div>
<div id="mainContainer" class="container">
<div id="mainContainerRepleace" class="row">
<table class="table table-striped">
<thead>
<tr>
<th data-field="id">ID</th>
<th data-field="username">账号</th>
<th data-field="name">姓名</th>
<th data-field="email">邮箱</th>
<th data-field="operation">操作</th>
</tr>
</thead>
<tbody>
<tr th:each="user : ${userModel.userList}">
<td th:text="${user.id}">1</td>
<td th:text="${user.username}">1</td><!-- 账号 -->
<td th:text="${user.name}">waylau</td><!-- 姓名 -->
<td th:text="${user.email}">waylau</td>
<td>
<div>
<a class="blog-edit-user" data-toggle="modal" data-target="#flipFlop" role="button" data-th-attr="userId=${user.id}">
<i class="fa fa-pencil-square-o" aria-hidden="true"></i>
</a>
<a class="blog-delete-user" role="button" data-th-attr="userId=${user.id}">
<i class="fa fa-times" aria-hidden="true"></i>
</a>
</div>
</td>
</tr>
</tbody>
</table>
<div th:replace="~{fragments/page :: page}">...</div>
</div>
</div>
<!-- The modal -->
<div class="modal fade" id="flipFlop" tabindex="-1" role="dialog"
aria-labelledby="modalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" id="modalLabel">新增/编辑</h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body" id="userFormContainer"></div>
<div class="modal-footer">
<button class="btn btn-primary" data-dismiss="modal" id="submitEdit">提交</button>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<!-- JavaScript -->
<script src="../../js/users/main.js" th:src="@{/js/users/main.js}"></script>
list方法中
async = false pageIndex = '0' pageSize = '10' name = ''
UserService接口如下
package com.waylau.spring.boot.blog.service;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import com.waylau.spring.boot.blog.domain.User;
/**
* 用户服务接口
* @author Administrator
*
*/
public interface UserService {
/**
* 新增 编辑 保存用户
* @param user
* @return
*/
User saveOrUpdateUser(User user);
/**
* 注册用户
* @param user
* @return
*/
User registerUser(User user);
/**
* 删除用户
* @param user
*/
void removeUser(Long id);
/**
* 根据id获取用户
* @param id
* @return
*/
User getUserById(Long id);
/**
* 根据用户名进行分页模糊查询
* @param name
* @param pageable
* @return
*/
Page<User> listUsersByNameLike(String name, Pageable pageable);
}
实现类UserServiceImpl如下
package com.waylau.spring.boot.blog.service;
import javax.transaction.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import com.waylau.spring.boot.blog.domain.User;
import com.waylau.spring.boot.blog.repository.UserRepository;
/**
* 用户服务接口实现
* @author Administrator
*
*/
@Service
public class UserServiceImpl implements UserService{
@Autowired
private UserRepository userRepository;
@Override
@Transactional
public User saveOrUpdateUser(User user) {
return userRepository.save(user);
}
@Override
@Transactional
public User registerUser(User user) {
return userRepository.save(user);
}
@Override
@Transactional
public void removeUser(Long id) {
userRepository.delete(id);
}
@Override
public User getUserById(Long id) {
return userRepository.findOne(id);
}
@Override
public Page<User> listUsersByNameLike(String name, Pageable pageable) {
//模糊查询
name = "%" + name + "%";
Page<User> users = userRepository.findByNameLike(name, pageable);
return users;
}
}
UserRepository如下,添加了两个自定义的方法,其余采用JPA提供的方法
package com.waylau.spring.boot.blog.repository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import com.waylau.spring.boot.blog.domain.User;
/**
* 用户接口
* @author Administrator
*/
public interface UserRepository extends JpaRepository<User, Long>{
/**
* 根据用户姓名分页查询用户列表
* @param name
* @param pageable
* @return
*/
Page<User> findByNameLike(String name, Pageable pageable);
/**
* 根据用户账号查询用户
* @param username
* @return
*/
User findByUsername(String username);
}
index方法调用完毕后,ajax返回分页page和用户列表 以及users/list 整个页面作为数据传递到admins/index页面
model.addAttribute("page", page);
model.addAttribute("userList", list);
list方法中的async = false 此处表示 若异步请求则返回users/list页面中的部分区域 此处是同步 则返回整个list页面
return new ModelAndView(async==true?"users/list :: #mainContainerRepleace":"users/list", "userModel", model);
页面加载后会对第一个菜单触发点击事件
$(".blog-menu .list-group-item:first").trigger("click");
此时会将整个users/list页面及其数据加载到admins/index里面的rightContainer里面
----------------------------------------------------------------
添加用户如下
<a class="btn btn-primary" data-toggle="modal" data-target="#flipFlop" role="button" id="addUser">
<i class="fa fa-plus" aria-hidden="true"></i>
</a>
其中js位于users/list.html 中的
<script src="../../js/users/main.js" th:src="@{/js/users/main.js}"></script>
如下:
/*!
* Bolg main JS.
*
* @since: 1.0.0 2017/3/9
* @author Way Lau <https://waylau.com>
*/
"use strict";
//# sourceURL=main.js
// DOM 加载完再执行
//DOM 加载完再执行
$(function() {
var _pageSize; // 存储用于搜索
// 根据用户名、页面索引、页面大小获取用户列表
function getUersByName(pageIndex, pageSize) {
$.ajax({
url: "/users",
contentType : 'application/json',
data:{
"async":true,
"pageIndex":pageIndex,
"pageSize":pageSize,
"name":$("#searchName").val()
},
success: function(data){
$("#mainContainer").html(data);
},
error : function() {
toastr.error("error!");
}
});
}
// 分页
$.tbpage("#mainContainer", function (pageIndex, pageSize) {
getUersByName(pageIndex, pageSize);
console.log(pageIndex);
console.log(pageSize);
_pageSize = pageSize;
});
// 搜索
$("#searchNameBtn").click(function() {
getUersByName(0, _pageSize);
});
// 获取添加用户的界面
$("#addUser").click(function() {
$.ajax({
url: "/users/add",
success: function(data){
$("#userFormContainer").html(data);
},
error : function(data) {
toastr.error("error!");
}
});
});
// 获取编辑用户的界面
$("#rightContainer").on("click",".blog-edit-user", function () {
$.ajax({
url: "/users/edit/" + $(this).attr("userId"),
success: function(data){
$("#userFormContainer").html(data);
},
error : function() {
toastr.error("error!");
}
});
});
// 提交变更后,清空表单
$("#submitEdit").click(function() {
$.ajax({
url: "/users",
type: 'POST',
data:$('#userForm').serialize(),
success: function(data){
$('#userForm')[0].reset();
if (data.success) {
// 从新刷新主界面
getUersByName(0, _pageSize);
} else {
toastr.error(data.message);
}
},
error : function() {
toastr.error("error!");
}
});
});
// 删除用户
$("#rightContainer").on("click",".blog-delete-user", function () {
$.ajax({
url: "/users/" + $(this).attr("userId") ,
type: 'DELETE',
success: function(data){
if (data.success) {
// 从新刷新主界面
getUersByName(0, _pageSize);
} else {
toastr.error(data.message);
}
},
error : function() {
toastr.error("error!");
}
});
});
});
添加按钮中js如下
// 获取添加用户的界面
$("#addUser").click(function() {
$.ajax({
url: "/users/add",
success: function(data){
$("#userFormContainer").html(data);
},
error : function(data) {
toastr.error("error!");
}
});
});
进入控制器UserController 中 调用方法
/**
* 获取创建用户表单界面
* @param model
* @return
*/
@GetMapping("/add")
public ModelAndView createForm(Model model){
model.addAttribute("user", new User(null,null,null,null));
return new ModelAndView("users/add", "userModel", model);
}
创建一个新用户属性都是 null,并传入页面users/add.html 作为数据页面返回给 users/list.html 中id = 'userFormContainer'的div里面
users/add.html如下
<div class="container">
<form th:action="@{/users}" method="post" th:object="${userModel.user}" id="userForm">
<input type="hidden" name="id" th:value="*{id}">
<div class="form-group ">
<label for="username" class="col-form-label">账号</label>
<input type="text" class="form-control" id="username" name="username" th:value="*{username}" maxlength="50" placeholder="请输入账号,至少3个字符,至多20个">
</div>
<div class="form-group">
<label for="email" class="col-form-label">邮箱</label>
<input type="email" class="form-control" id="email" name="email" th:value="*{email}" maxlength="50" placeholder="请输入邮箱">
</div>
<div class="form-group">
<label for="name" class="col-form-label">姓名</label>
<input type="text" class="form-control" id="name" name="name" th:value="*{name}" maxlength="20" placeholder="请输入姓名,至少2个字符,至多20个" >
</div>
<div class="form-group">
<label for="password" class="col-form-label">密码</label>
<input type="password" class="form-control" id="password" name="password" th:value="*{password}" maxlength="30" placeholder="请输入密码,字母或特殊符号和数字结合" >
</div>
</form>
</div><!-- /.container -->
之前 users/list.html 中id = userFormContainer 所在的父节点div 样式display : none 追加了users/add.html后,变为block 可见
这是通过 data-target="#flipFlop" 来实现的 flipFlop是需要显示的DIV 的id
User如下:
package com.waylau.spring.boot.blog.domain;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.validation.constraints.Size;
import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.NotEmpty;
/**
* 用户实体
* @author Administrator
* Spring注解 @NotBlank,@NotNull,@NotEmpty
*/
@Entity//实体
public class User {
@Id//主键
@GeneratedValue(strategy = GenerationType.IDENTITY)//主键的生成策略(自增)
private Long id;//实体一个唯一标识
@NotEmpty(message = "姓名不能为空")//不能为null,也不能为空
@Size(min = 2, max = 20)
@Column(nullable = false, length = 20)//映射为字段 值不能为空
private String name;
@NotEmpty(message = "邮箱不能为空")
@Size(max = 50)
//nullable表示该字段是否可以为null值,默认为true unique表示该字段是否为唯一标识,默认为false
@Email(message = "邮箱格式不对")
@Column(nullable = false, length = 50, unique = true)
private String email;
@NotEmpty(message = "账号不能为空")
@Size(min = 3, max = 20)
@Column(nullable = false, length = 20, unique = true)
private String username;//用户账号 用户登录的唯一标识
@NotEmpty(message = "密码不能为空")
@Size(max = 100)
@Column(length = 100)//字段长度
private String password;//密码
@Column(length = 200)
private String avatar;//头像信息
protected User(){}//无参构造 设为protected防止直接使用
public User(Long id, String name, String username, String email) {
this.id = id;
this.name = name;
this.email = email;
this.username = username;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getAvatar() {
return avatar;
}
public void setAvatar(String avatar) {
this.avatar = avatar;
}
@Override
public String toString() {
return "User [id=" + id + ", name=" + name + ", email=" + email + ", username=" + username + ", password="
+ password + ", avatar=" + avatar + "]";
}
}
填好表单提交 POST /users
调用js如下
// 提交变更后,清空表单
$("#submitEdit").click(function() {
$.ajax({
url: "/users",
type: 'POST',
data:$('#userForm').serialize(),
success: function(data){
$('#userForm')[0].reset();
if (data.success) {
// 从新刷新主界面
getUersByName(0, _pageSize);
} else {
toastr.error(data.message);
}
},
error : function() {
toastr.error("error!");
}
});
});
进入UserController 保存和修改都调用该方法
/**
* 保存或修改用户
* @param user
* @return
*/
@PostMapping
public ResponseEntity<Response> saveOrUpdateUser(User user){
try{
userService.saveOrUpdateUser(user);
} catch (ConstraintViolationException e) {
return ResponseEntity.ok().body(new Response(false, ConstraintViolationExceptionHandler.getMessage(e)));
}
return ResponseEntity.ok().body(new Response(true, "处理成功", user));
}
将返回的内容封装成类Response如下
package com.waylau.spring.boot.blog.vo;
/**
* 返回对象
* @author Administrator
*
*/
public class Response {
private boolean success;//处理是否成功
private String message;//处理后的消息提示
private Object body;//返回的数据
public Response(boolean success, String message) {
this.success = success;
this.message = message;
}
public Response(boolean success, String message, Object body) {
this.success = success;
this.message = message;
this.body = body;
}
public boolean isSuccess() {
return success;
}
public void setSuccess(boolean success) {
this.success = success;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Object getBody() {
return body;
}
public void setBody(Object body) {
this.body = body;
}
}
若填写表单中不符合规定,则会在此处抛出异常,自定义bean处理异常如下
package com.waylau.spring.boot.blog.util;
import java.util.ArrayList;
import java.util.List;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import org.apache.commons.lang3.StringUtils;
/**
* ConstraintViolationException bean异常处理器
* @author Administrator
*
*/
public class ConstraintViolationExceptionHandler {
/**
* 获取批量异常信息
* @param e
* @return
*/
public static String getMessage(ConstraintViolationException e){
List<String> msgList = new ArrayList<String>();
for(ConstraintViolation<?> constraintViolation : e.getConstraintViolations()){
msgList.add(constraintViolation.getMessage());
}
String message = StringUtils.join(msgList.toArray(), ";");
return message;
}
}
可能多个异常,以;拼接返回
修改功能和添加功能类似 修改、查询等不再讲解,查看代码即可
关于js中的_pageSize 就是每页显示的条数,前端js中默认是undefined,当切换每页显示条数的时候
就会调用
// 分页
$.tbpage("#mainContainer", function (pageIndex, pageSize) {
getUersByName(pageIndex, pageSize);
console.log(pageIndex);
console.log(pageSize);
_pageSize = pageSize;
});
给js中的全局变量_pageSize赋值
若直接搜索的时候,可能_pageSize还没有赋值,就是undefined,传入后台
url: "/users",
contentType : 'application/json',
data:{
"async":true,
"pageIndex":pageIndex,
"pageSize":pageSize,
"name":$("#searchName").val()
},
实际传递参数
后台接收参数 中name 为空字符串
true
0
10