目录
前端使用thymeleaf模板引擎+bootstrap组件+jquery,后端使用springboot+mybatis+mysql+redis缓存+pageHelper分页插件+swagger接口测试
13.运行地址(使用花生壳进行内网穿透并配置端口,需本地开启tomcat才能远程访问)
(1) lombok的使用(编辑器需下载lombok插件,pom文件导入lombok依赖)
1.技术栈
前端使用thymeleaf模板引擎+bootstrap组件+jquery,后端使用springboot+mybatis+mysql+redis缓存+pageHelper分页插件+swagger接口测试
2.项目结构
3.pom依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--整合数据源-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!--整合mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<!-- pagehelper 分页插件-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.11</version>
<scope>runtime</scope>
</dependency>
<!--添加tomcat依赖模块.-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--swagger依赖-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<!--redis缓存-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!--redis启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
4.application.properties
server.port=1772
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql:///数据库名称?serverTimezone=GMT%2B8&useSSL=false
spring.datasource.username=用户名
spring.datasource.password=密码
#扫描的包
mybatis.type-aliases-package=com.cn.domain
#映射文件所在位置
mybatis.mapper-locations=classpath:com/cn/dao/*.xml
#thymeleaf模板引擎配置
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.mode=HTML5
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.content-type=text/html
spring.thymeleaf.cache=false
spring.mvc.static-path-pattern=/static/**
#pagehelper分页插件配置
pagehelper.helper-dialect=mysql
pagehelper.reasonable=true
pagehelper.support-methods-arguments=true
#swagger2配置
spring.mvc.pathmatch.matching-strategy=ant_path_matcher
#redis配置
spring.redis.host=127.0.0.1
spring.redis.port=6379
#spring.redis.password=root
spring.redis.database=2
#连接超时时间(毫秒
spring.redis.timeout=1000
#连接池最大链接数(负数表示没有限制)
spring.redis.lettuce.pool.max-active=8
#连接池最大阻塞等待时间(负数表示没有限制)
spring.redis.lettuce.pool.max-wait=-1
#连接池最大空闲连接数
spring.redis.lettuce.pool.max-idle=8
#连接池最小空闲连接数
spring.redis.lettuce.pool.min-idlee=0
5.domain层
User.java
public class User implements Serializable{
private Integer id;
private String name;
private String gender;
private Integer age;
private String address;
private String qq;
private String email;
//省略get和set,需自己补充或者使用lombok插件
}
Admin.java
public class Admin {
private String username;
private String password;
//省略get和set,需自己补充或者使用lombok插件
}
6.controller层
package com.cn.controller;
import com.cn.domain.Admin;
import com.cn.domain.User;
import com.cn.service.UserService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
@Controller
public class UserController {
@Autowired
private UserService userService;
/**
* 登录
* @return
*/
@RequestMapping("/login.do")
public String login(Admin admin,HttpServletRequest request){
Admin loginUser = userService.findUsernameAndPassword(admin);
if(loginUser!=null){
return "redirect:/pageByCondition";
}else {
request.setAttribute("login_msg","用户名或密码错误!");
return "login";
}
}
/**
* 注册
* @return
*/
@RequestMapping("/regist.do")
public String regist(Admin admin,HttpServletRequest request){
//判断数据库中是否包含该用户
Admin user = userService.findUsername(admin.getUsername());
if(user!=null){
//数据库中存在该用户,跳转到regist.jsp页面,提示该用户已被注册,请重新填写注册信息
request.setAttribute("regist_msg","该用户已被注册,请重新填写注册信息");
return "regist";
}else {
if(!admin.getUsername().isEmpty()&&!admin.getPassword().isEmpty()){
//数据库中不存在该用户,则注册成功,将数据保存在数据库中,并且重定向到登录页面
userService.saveRegistUser(admin);
return "login";
} else {
request.setAttribute("regist_msg","用户名或密码不能为空");
return "regist";
}
}
}
/**
* 添加用户
* @param name
* @param gender
* @param age
* @param address
* @param qq
* @param email
* @return
*/
@RequestMapping("/add.do")
public String addUser(String name,String gender,String age,String address,String qq,String email){
int a;
if ("".equals(age)||age==null){
a = 0;
}else {
a = Integer.parseInt(age);
}
userService.addUser(name,gender,a,address,qq,email);
return "redirect:/pageByCondition";
}
/**
* 更新用户
* @param id
* @param name
* @param gender
* @param age
* @param address
* @param qq
* @param email
* @return
*/
@RequestMapping("/update.do")
public String updateUser(Integer id,String name,String gender,Integer age,String address,String qq,String email){
userService.updateUser(id,name,gender,age,address,qq,email);
return "redirect:/pageByCondition";
}
/**
* 根据id查询
* @param id
* @param model
* @return
*/
@RequestMapping("/findById")
public String findById(Integer id,Model model){
User userById = userService.findById(id);
model.addAttribute("user",userById);
return "update";
}
/**
* 根据id删除用户
* @param id
* @return
*/
@RequestMapping("/delUser")
public String delUser(Integer id){
userService.delUserById(id);
return "redirect:/pageByCondition";
}
/**
* 删除选中的多个用户
* @param uid
* @return
*/
@RequestMapping("/delSelectedUser")
public String delSelectedUser(String[] uid){
for (String s : uid) {
userService.delUserById(Integer.parseInt(s));
}
return "redirect:/pageByCondition";
}
/**
* 多条件查询和分页
* @param pageNum
* @param name
* @param age
* @param address
* @param model
* @return
*/
@RequestMapping("/pageByCondition")
public String pageByCondition(@RequestParam(value = "pageNum",defaultValue = "1")Integer pageNum, @RequestParam(value = "name",defaultValue = "") String name, @RequestParam(value = "age",defaultValue = "0")Integer age, @RequestParam(value = "address",defaultValue = "")String address, Model model){
PageHelper.startPage(pageNum,10); //第一个参数代表当前页码 第二个参数代表每页多少条记录数
List<User> u = userService.findCondition(name,age,address);
PageInfo<User> pageInfo= new PageInfo(u);
model.addAttribute("pageInfo",pageInfo);
return "list";
}
}
7.service层
UserService.java
@Service
@Transactional
public class UserService {
@Autowired
private UserDao userDao;
/**
* 查询用户名和密码
* @return
*/
public Admin findUsernameAndPassword(Admin admin) {
return userDao.findUsernameAndPassword(admin);
}
/**
* 查询用户名
* @return
*/
public Admin findUsername(String username) {
return userDao.findUsername(username);
}
/**
* 保存注册用户信息
*/
public void saveRegistUser(Admin admin) {
this.userDao.saveRegistUser(admin);
}
/**
* 查询所有
* @return
*/
@Cacheable(cacheNames = "allUser")
public List<User> findAll() {
System.out.println("正在使用mysql查找......");
return userDao.findAll();
}
/**
* 添加用户
* @param name
* @param gender
* @param age
* @param address
* @param qq
* @param email
*/
public void addUser(String name,String gender,Integer age,String address,String
qq,String email){
this.userDao.addUser(name,gender,age,address,qq,email);
}
/**
* 条件查询
* @param name
* @param age
* @param address
* @return
*/
public List<User> findCondition(String name,Integer age,String address){
return userDao.findCondition(name,age,address);
}
/**
* 更新用户
* @param id
* @param name
* @param gender
* @param age
* @param address
* @param qq
* @param email
*/
public void updateUser(Integer id,String name,String gender,Integer age,String
address,String qq,String email){
this.userDao.updateUser(id,name,gender,age,address,qq,email);
}
/**
* 通过id查找用户
* @param id
* @return
*/
public User findById(Integer id){
return userDao.findById(id);
}
/**
* g根据id删除用户
* @param id
* @return
*/
public void delUserById(Integer id){
this.userDao.delUserById(id);
}
}
8.dao层
UserDao.java
@Mapper
public interface UserDao {
/**
* 查询用户名和密码
* @return
*/
Admin findUsernameAndPassword(Admin admin);
/**
* 查询用户名
* @return
*/
Admin findUsername(String username);
/**
* 保存注册用户信息
*/
void saveRegistUser(Admin admin);
/**
* 查询所有
* @return
*/
List<User> findAll();
/**
* 添加用户
* @param name
* @param gender
* @param age
* @param address
* @param qq
* @param email
*/
void addUser(@Param("name") String name, @Param("gender") String gender,
@Param("age") Integer age, @Param("address") String address, @Param("qq") String qq,
@Param("email") String email);
/**
* 条件查询
* @param name
* @param age
* @param address
* @return
*/
List<User> findCondition(@Param("name") String name, @Param("age") Integer age,
@Param("address") String address);
/**
* 更新用户
* @param id
* @param name
* @param gender
* @param age
* @param address
* @param qq
* @param email
*/
void updateUser(@Param("id") Integer id, @Param("name") String name, @Param("gender")
String gender, @Param("age") Integer age, @Param("address") String address,
@Param("qq") String qq, @Param("email") String email);
/**
* 根据Id查找用户
* @param id
* @return
*/
User findById(Integer id);
/**
* g根据id删除用户
* @param id
* @return
*/
void delUserById(Integer id);
}
9.UserDao.xml
<mapper namespace="com.cn.dao.UserDao">
<!--配置查询所有,这个id是UserDao里面的方法名称,这里的resultType是查询后返回的类型,必须是
全限定类名-->
<select id="findAll" resultType="user">
SELECT * from user
</select>
<!-- 根据用户名和密码进行查找 -->
<!--parameterType 属性:代表参数的类型,因为我们要传入的参数是String,所以类型就写
String。-->
<!--resultType 属性:用于指定结果集的类型。-->
<select id="findUsernameAndPassword" parameterType="java.lang.String"
resultType="admin">
SELECT * from `admin` WHERE username = #{username} and password = #{password}
</select>
<!--查询用户名-->
<select id="findUsername" parameterType="java.lang.String" resultType="admin">
SELECT * from `admin` where username = #{username}
</select>
<!--保存注册用户信息-->
<insert id="saveRegistUser" parameterType="com.cn.domain.Admin">
insert into `admin` (username,password) values(#{username},#{password})
</insert>
<!--添加用户-->
<sql id="key">
<trim suffixOverrides=",">
<if test="name!=null and name!=''">
name,
</if>
<if test="gender!=null and gender!=''">
gender,
</if>
<if test="age!=null and age!='' or age!=0">
age,
</if>
<if test="address!=null and address!=''">
address,
</if>
<if test="qq!=null and qq!=''">
qq,
</if>
<if test="email!=null and email!=''">
email,
</if>
</trim>
</sql>
<sql id="values">
<trim suffixOverrides=",">
<if test="name!=null and name!=''">
#{name},
</if>
<if test="gender!=null and gender!=''">
#{gender},
</if>
<if test="age!=null and age!='' or age!=0">
#{age},
</if>
<if test="address!=null and address!=''">
#{address},
</if>
<if test="qq!=null and qq!=''">
#{qq},
</if>
<if test="email!=null and email!=''">
#{email},
</if>
</trim>
</sql>
<insert id="addUser" parameterType="com.cn.domain.User">
INSERT into user (<include refid="key"/>) VALUES (<include refid="values"/>)
</insert>
<!--条件查询-->
<select id="findCondition" resultType="user">
SELECT * from user
<where>
<if test="name!=null and name!=''">
and name=#{name}
</if>
<if test="age!=null and age!=''">
and age=#{age}
</if>
<if test="address!=null and address!=''">
and address=#{address}
</if>
</where>
</select>
<!--更新用户-->
<update id="updateUser" parameterType="com.cn.domain.User">
UPDATE USER
<set>
<if test="name!=null and name!=''">
name=#{name},
</if>
<if test="gender!=null and gender!=''">
gender=#{gender},
</if>
<if test="age!=null and age!='' or age!=0">
age=#{age},
</if>
<if test="address!=null and address!=''">
address=#{address},
</if>
<if test="qq!=null and qq!=''">
qq=#{qq},
</if>
<if test="email!=null and email!=''">
email=#{email},
</if>
</set>
where id=#{id}
</update>
<!--根据id查询-->
<select id="findById" parameterType="java.lang.Integer" resultType="user">
SELECT * from user where id=#{id}
</select>
<update id="delUserById" parameterType="java.lang.Integer">
DELETE from `user` where id=#{id}
</update>
</mapper>
10.前端页面
login.html
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<!-- 引入bootstrap样式 -->
<link rel="stylesheet"
href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script type="text/javascript"
src="https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script>
<script type="text/javascript"
src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<meta charset="UTF-8">
<title>用户登录首页</title>
</head>
<style type="text/css">
* {
margin: 0px;
padding: 0px;
}
body {
background-image: url(../static/img/bg.png);
}
#login {
width: 450px;
height: 400px;
border-radius: 15px;
background: transparent;
margin: 0px auto;
margin-top: 180px;
border: 1px solid white;
}
#login #top {
width: 420px;
height: 60px;
line-height: 60px;
/*border: 1px solid red;*/
margin: 0px auto;
margin-top: 65px;
}
#login #top img {
margin-left: 90px;
margin-top: -20px;
float: left;
}
#login #top span {
font-size: 40px;
font-weight: bold;
font-family: 微软雅黑;
}
#login #bottom {
width: 420px;
height: 205px;
/*border: 1px solid red;*/
margin: 0px auto;
margin-top: 10px;
padding: 20px;
}
.form-group{
padding: 5px auto;
}
.col-sm-6,.col-sm-2{
margin-left: 40px;
}
.btn{
margin-left: 80px;
}
#regist{
text-align: center;
}
strong{
display: block;
margin: 20px auto 0;
text-align: center;
}
</style>
<div id="login">
<div id="top">
<img src="../static/img/cloud.png"/><span>LOGIN</span>
</div>
<div id="bottom">
<form method="post" th:action="@{/login.do}" class="form-horizontal"
autocomplete="off">
<div class="form-group">
<label for="inputUsername3" class="col-sm-2 control-
label">Username</label>
<div class="col-sm-6">
<input type="text" class="form-control" id="inputUsername3"
name="username" placeholder="Username">
</div>
</div>
<div class="form-group">
<label for="inputPassword3" class="col-sm-2 control-
label">Password</label>
<div class="col-sm-6">
<input type="password" class="form-control" id="inputPassword3"
name="password" placeholder="Password">
</div>
</div>
<div class="form-group" id="submit">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn">Sign in</button>
</div>
</div>
<div id="regist">
<a th:href="@{regist}">没账号?点击此处注册</a>
</div>
</form>
<strong th:text="${login_msg}"></strong>
</div>
</div>
</body>
</html>
regist.html
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<!-- 引入bootstrap样式 -->
<link rel="stylesheet"
href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script type="text/javascript"
src="https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script>
<script type="text/javascript"
src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<meta charset="UTF-8">
<title>用户注册页面</title>
</head>
<style type="text/css">
* {
margin: 0px;
padding: 0px;
}
body {
background-image: url(../static/img/bg.png);
}
#login {
width: 450px;
height: 400px;
border-radius: 15px;
background: transparent;
margin: 0px auto;
margin-top: 180px;
border: 1px solid white;
}
#login #top {
width: 420px;
height: 60px;
line-height: 60px;
/*border: 1px solid red;*/
margin: 0px auto;
margin-top: 65px;
}
#login #top img {
margin-left: 90px;
margin-top: -20px;
float: left;
}
#login #top span {
font-size: 40px;
font-weight: bold;
font-family: 微软雅黑;
}
#login #bottom {
width: 420px;
height: 205px;
/*border: 1px solid red;*/
margin: 0px auto;
margin-top: 10px;
padding: 20px;
}
.form-group{
padding: 5px auto;
}
.col-sm-6,.col-sm-2{
margin-left: 40px;
}
.btn{
margin-left: 80px;
}
strong{
display: block;
margin: 20px auto 0;
text-align: center;
}
</style>
<body>
<div id="login">
<div id="top">
<img src="../static/img/cloud.png"/><span>REGIST</span>
</div>
<div id="bottom">
<form method="post" th:action="@{/regist.do}" class="form-horizontal"
autocomplete="off">
<div class="form-group">
<label for="inputUsername3" class="col-sm-2 control-
label">Username</label>
<div class="col-sm-6">
<input type="text" class="form-control" id="inputUsername3"
name="username" placeholder="Username">
</div>
</div>
<div class="form-group">
<label for="inputPassword3" class="col-sm-2 control-
label">Password</label>
<div class="col-sm-6">
<input type="password" class="form-control" id="inputPassword3"
name="password" placeholder="Password">
</div>
</div>
<div class="form-group" id="submit">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn">regist</button>
</div>
</div>
</form>
<strong th:text="${regist_msg}"></strong>
</div>
</div>
</body>
</html>
list.html
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<!-- 引入bootstrap样式 -->
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script type="text/javascript" src="https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script>
<script type="text/javascript" src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<meta charset="UTF-8">
<title>用户信息管理系统</title>
<style type="text/css">
td, th {
text-align: center;
}
</style>
<script>
$(function () {
//全选与全不选
$("#firstCb").click(function () {
var u = $("input[name='uid']")
for (var i = 0; i < u.length; i++) {
u[i].checked = this.checked;
}
});
//1. 获取数据行的奇数行的tr
$("tr:gt(0):odd").css("backgroundColor","#e8f4ff");
//2. 获取数据行的偶数行的tr
$("tr:gt(0):even").css("backgroundColor","#dbe8f2");
})
function deleteSel() {
if ($("input[type='checkbox']:checked").length < 1) {
alert("请选择要删除的的用户!")
} else {
if (confirm("您确定要删除吗?")) {
$("#listForm").submit();
} else {
}
}
}
</script>
</head>
<body>
<div class="container">
<h3 style="text-align: center;color: black;font-family: 黑体">用户信息列表</h3>
<div style="float: left;margin-bottom: 20px;margin-top: 10px">
<form th:action="@{/pageByCondition}" class="form-inline" method="post" id="form" autocomplete="off">
<div class="form-group">
<label for="exampleInputName2">姓名</label>
<input type="text" name="name" class="form-control" id="exampleInputName2">
</div>
<div class="form-group">
<label for="exampleInputEmail2">年龄</label>
<input type="text" name="age" class="form-control" id="exampleInputEmail2">
</div>
<div class="form-group">
<label for="exampleInputName3">籍贯</label>
<input type="text" name="address" class="form-control" id="exampleInputName3">
</div>
<button type="submit" class="btn btn-primary">查询</button>
</form>
</div>
<div style="float: right;margin-bottom: 20px;margin-top: 10px">
<a class="btn btn-primary" th:href="@{add}">添加联系人</a>
<a class="btn btn-primary" onclick="deleteSel()">删除选中</a>
</div>
<form id="listForm" method="post" th:action="@{/delSelectedUser}" autocomplete="off">
<table border="1" class="table table-bordered table-hover">
<tr style="background-color: #76aef0">
<th><input type="checkbox" id="firstCb"></th>
<th>编号</th>
<th>姓名</th>
<th>性别</th>
<th>年龄</th>
<th>籍贯</th>
<th>QQ</th>
<th>邮箱</th>
<th>操作</th>
</tr>
<tr th:if="${pageInfo.list != null}" th:each="u,uStat:${pageInfo.list}">
<td><input type="checkbox" name="uid" th:value="${u.id}"></td>
<td th:text="${uStat.count}"></td>
<td th:text="${u.name}"></td>
<td th:text="${u.gender}"></td>
<td th:text="${u.age}"></td>
<td th:text="${u.address}"></td>
<td th:text="${u.qq}"></td>
<td th:text="${u.email}"></td>
<td><a class="btn btn-success btn-sm" th:href="@{findById(id=${u.id})}">修改</a>
<a class="btn btn-info btn-sm" href="#" th:href="@{delUser(id=${u.id})}"
onclick="return del()">删除</a></td>
</tr>
<script type="text/javascript">
function del() {
if (confirm("您确定要删除吗?")) {
return true;
} else {
return false;
}
}
</script>
</table>
</form>
<div>
<nav aria-label="Page navigation">
<ul class="pagination">
<li th:if="${pageInfo.pages} eq '1'" class="disabled"><span aria-hidden="true">«</span></li>
<!--上一页-->
<li th:if="${pageInfo.pages} ne '1'">
<a th:href="@{pageByCondition(pageNum=${pageInfo.prePage},name=${name},age=${age},address=${address})}" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
<!--遍历页数-->
<!--page:临时变量 p:状态变量 pageInfo:要遍历的数据-->
<li th:each="page,p:${pageInfo.navigatepageNums}">
<a th:href="@{pageByCondition(pageNum=${p.count},name=${name},age=${age},address=${address})}" th:text="${p.count}"></a>
</li>
<li th:if="${pageInfo.pages} eq '1'" class="disabled"><span aria-hidden="true">»</span></li>
<!--下一页-->
<li th:if="${pageInfo.pages} ne '1'">
<a th:href="@{pageByCondition(pageNum=${pageInfo.nextPage},name=${name},age=${age},address=${address})}" aria-label="Previous">
<span aria-hidden="true">»</span>
</a>
</li>
<span style="font-size: 25px;margin-left: 5px;"
th:text="'共'+${pageInfo.total}+'条记录,共'+${pageInfo.pages}+'页'"></span>
</ul>
</nav>
</div>
</div>
<script>
$(function () {
$("li").click(function () {
$(this).attr("class", "active")
})
})
</script>
</body>
</html>
add.html
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<!-- 引入bootstrap样式 -->
<link rel="stylesheet"
href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script type="text/javascript"
src="https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script>
<script type="text/javascript"
src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<meta charset="UTF-8">
<title>添加用户</title>
<style type="text/css">
* {
margin: 0px;
padding: 0px;
}
body {
background-image: url(../static/img/bg.png);
}
#add {
width: 600px;
height: 600px;
border-radius: 15px;
background: transparent;
margin: 0px auto;
margin-top: 40px;
border: 1px solid white;
}
#add #top {
width: 420px;
height: 60px;
line-height: 60px;
/*border: 1px solid red;*/
margin: 0px auto;
margin-top: 65px;
}
#add #top img {
margin-left: 30px;
margin-top: -20px;
float: left;
}
#add #top span {
font-size: 40px;
font-weight: bold;
font-family: 微软雅黑;
}
#add #bottom {
width: 420px;
height: 205px;
/*border: 1px solid red;*/
margin: 0px auto;
margin-top: 10px;
padding: 20px;
}
.form-group{
padding: 5px auto;
}
.col-sm-6,.col-sm-2{
margin-left: 35px;
}
.control-label{
width: 100px;
}
.btn{
margin-left: 40px;
}
.bt{
padding-left: 90px;
}
</style>
</head>
<body>
<div id="add">
<div id="top">
<img src="../static/img/cloud.png" /><span>ADDUSER</span>
</div>
<div id="bottom">
<form method="post" th:action="@{/add.do}" class="form-horizontal"
autocomplete="off">
<!--<input type="hidden" name="id">-->
<div class="form-group">
<label for="name" class="col-sm-2 control-label">姓名:</label>
<div class="col-sm-6">
<input type="text" class="form-control" id="name" name="name"
placeholder="请输入姓名">
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">性别:</label>
<div class="col-sm-6">
<input type="radio" name="gender" value="男" checked="checked"/>男
<input type="radio" name="gender" value="女"/>女
</div>
</div>
<div class="form-group">
<label for="age" class="col-sm-2 control-label">年龄:</label>
<div class="col-sm-6">
<input type="text" class="form-control" id="age" name="age"
placeholder="请输入年龄">
</div>
</div>
<div class="form-group">
<label for="address" class="col-sm-2 control-label">籍贯:</label>
<div class="col-sm-6">
<input type="text" class="form-control" id="address" name="address"
placeholder="请输入籍贯"/>
</div>
</div>
<div class="form-group">
<label for="qq" class="col-sm-2 control-label">QQ:</label>
<div class="col-sm-6">
<input type="text" class="form-control" id="qq" name="qq"
placeholder="请输入QQ号码"/>
</div>
</div>
<div class="form-group">
<label for="email" class="col-sm-2 control-label">Email:</label>
<div class="col-sm-6">
<input type="text" class="form-control" id="email" name="email"
placeholder="请输入邮箱地址"/>
</div>
</div>
<div class="form-group bt">
<input class="btn btn-primary" type="submit" value="提交"/>
<input class="btn btn-default" type="reset" value="重置" />
<input class="btn btn-default" type="button" value="返回"
onclick="goback()"/>
</div>
<script>
function goback() {
history.back();
}
</script>
</form>
</div>
</div>
</body>
</html>
update.html
<!DOCTYPE html>
<!-- 网页使用的语言 -->
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<!-- 引入bootstrap样式 -->
<link rel="stylesheet"
href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script type="text/javascript"
src="https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script>
<script type="text/javascript"
src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<meta charset="UTF-8">
<title>添加用户</title>
<style type="text/css">
* {
margin: 0px;
padding: 0px;
}
body {
background-image: url(../static/img/bg.png);
}
#add {
width: 600px;
height: 600px;
border-radius: 15px;
background: transparent;
margin: 0px auto;
margin-top: 40px;
border: 1px solid white;
}
#add #top {
width: 420px;
height: 60px;
line-height: 60px;
/*border: 1px solid red;*/
margin: 0px auto;
margin-top: 65px;
}
#add #top img {
margin-left: 30px;
margin-top: -20px;
float: left;
}
#add #top span {
font-size: 40px;
font-weight: bold;
font-family: 微软雅黑;
}
#add #bottom {
width: 420px;
height: 205px;
/*border: 1px solid red;*/
margin: 0px auto;
margin-top: 10px;
padding: 20px;
}
.form-group{
padding: 5px auto;
}
.col-sm-6,.col-sm-2{
margin-left: 35px;
}
.control-label{
width: 100px;
}
.btn{
margin-left: 40px;
}
.bt{
padding-left: 90px;
}
</style>
</head>
<body>
<div id="add">
<div id="top">
<img src="../static/img/cloud.png" /><span>UPDATEUSER</span>
</div>
<div id="bottom">
<form method="post" th:action="@{/update.do}" class="form-horizontal"
autocomplete="off">
<input type="hidden" name="id" th:value="${user.id}">
<div class="form-group">
<label for="name" class="col-sm-2 control-label">姓名:</label>
<div class="col-sm-6">
<input type="text" class="form-control" id="name" name="name"
th:value="${user.name}" placeholder="请输入姓名">
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">性别:</label>
<div class="col-sm-6" th:if="${user.gender=='男'}">
<input type="radio" name="gender" value="男" checked />男
<input type="radio" name="gender" value="女"/>女
</div>
<div class="col-sm-6" th:if="${user.gender=='女'}">
<input type="radio" name="gender" value="男"/>男
<input type="radio" name="gender" value="女" checked/>女
</div>
</div>
<div class="form-group">
<label for="age" class="col-sm-2 control-label">年龄:</label>
<div class="col-sm-6">
<input type="text" class="form-control" id="age" name="age"
th:value="${user.age}" placeholder="请输入年龄">
</div>
</div>
<div class="form-group">
<label for="address" class="col-sm-2 control-label">籍贯:</label>
<div class="col-sm-6">
<input type="text" class="form-control" id="address" name="address"
th:value="${user.address}" placeholder="请输入籍贯"/>
</div>
</div>
<div class="form-group">
<label for="qq" class="col-sm-2 control-label">QQ:</label>
<div class="col-sm-6">
<input type="text" class="form-control" id="qq" name="qq"
th:value="${user.qq}" placeholder="请输入QQ号码"/>
</div>
</div>
<div class="form-group">
<label for="email" class="col-sm-2 control-label">Email:</label>
<div class="col-sm-6">
<input type="text" class="form-control" id="email" name="email"
th:value="${user.email}" placeholder="请输入邮箱地址"/>
</div>
</div>
<div class="form-group bt">
<input class="btn btn-primary" type="submit" value="提交" />
<input class="btn btn-default" type="reset" value="重置" />
<input class="btn btn-default" type="button" value="返回"
onclick="goback()"/>
</div>
<script>
function goback() {
history.back();
}
</script>
</form>
</div>
</div>
</body>
</html>
11.sql文件
user.sql
/*
Navicat MySQL Data Transfer
Source Server : localhost_3306
Source Server Version : 80011
Source Host : localhost:3306
Source Database : usermanage
Target Server Type : MYSQL
Target Server Version : 80011
File Encoding : 65001
Date: 2022-05-24 13:32:32
*/
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`gender` varchar(5) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
`address` varchar(32) DEFAULT NULL,
`qq` varchar(20) DEFAULT NULL,
`email` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=41 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1', '张三', '男', '20', '江西', '65452', 'asdad@qq.com');
INSERT INTO `user` VALUES ('2', '李四', '男', '20', '赣州', '8569', 'yolo@qq.com');
INSERT INTO `user` VALUES ('3', '刘亦菲', '女', '29', '北京', '5689', 'liuyifei@qq.com');
INSERT INTO `user` VALUES ('4', '刘诗诗', '女', '34', '天津', '4233', 'liushishi@qq.com');
INSERT INTO `user` VALUES ('5', '易烊千玺', '男', '20', '四川', '5689', 'jason@qq.com');
INSERT INTO `user` VALUES ('6', '胡歌', '男', '39', '湖北', '4233', 'fghhf@qq.com');
INSERT INTO `user` VALUES ('7', '孙燕姿', '女', '44', '台湾', '4537', 'ryfghh@qq.com');
INSERT INTO `user` VALUES ('8', '谢霆锋', '男', '32', '香港', '5689', 'adgsdd@qq.com');
INSERT INTO `user` VALUES ('9', '欧阳娜娜', '女', '22', '台湾', '4233', 'fghhf@qq.com');
INSERT INTO `user` VALUES ('10', '杨紫', '女', '28', '北京', '4537', 'ryfghh@qq.com');
INSERT INTO `user` VALUES ('11', '周冬雨', '女', '27', '四川', '5689', 'adgsdd@qq.com');
INSERT INTO `user` VALUES ('12', '古力娜扎', '女', '28', '新疆', '4233', 'fghhf@qq.com');
INSERT INTO `user` VALUES ('13', '迪丽热巴', '女', '27', '新疆', '4537', 'ryfghh@qq.com');
INSERT INTO `user` VALUES ('14', '王俊凯', '男', '22', '北京', '63256', 'yjgdbb@163.com');
INSERT INTO `user` VALUES ('15', '王源', '男', '21', '四川', '6894', 'yvncmh@qq.com');
INSERT INTO `user` VALUES ('16', '唐嫣', '女', '32', '北京', '8253', 'uticndu@qq.com');
INSERT INTO `user` VALUES ('17', '李一桐', '女', '28', '北京', '7268', 'qeegcdd@qq.com');
INSERT INTO `user` VALUES ('18', '白鹿', '女', '28', '北京', '6874', 'ytjdv@qq.com');
INSERT INTO `user` VALUES ('19', '李沁', '女', '28', '北京', '1347', 'dbddd@qq.com');
INSERT INTO `user` VALUES ('20', '欧豪', '男', '27', '北京', '4733', 'dhdfvb@qq.com');
INSERT INTO `user` VALUES ('21', '杨洋', '男', '28', '北京', '7700', 'xmcvb@qq.com');
INSERT INTO `user` VALUES ('22', '周深', '男', '26', '北京', '7934', 'zsdfsdg@qq.com');
INSERT INTO `user` VALUES ('23', '邓紫棋', '女', '27', '北京', '5678', 'xcbcb@qq.com');
INSERT INTO `user` VALUES ('24', '金志文', '男', '30', '北京', '4275', 'zcxbcn@qq.com');
INSERT INTO `user` VALUES ('25', '欧阳娜娜', '女', '23', '北京', '453433', 'xvcbvv@qq.com');
admin.sql
/*
Navicat MySQL Data Transfer
Source Server : localhost_3306
Source Server Version : 80011
Source Host : localhost:3306
Source Database : usermanage
Target Server Type : MYSQL
Target Server Version : 80011
File Encoding : 65001
Date: 2022-05-24 13:32:42
*/
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for admin
-- ----------------------------
DROP TABLE IF EXISTS `admin`;
CREATE TABLE `admin` (
`username` varchar(255) DEFAULT NULL,
`password` varchar(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of admin
-- ----------------------------
INSERT INTO `admin` VALUES ('admin', '6666');
12.效果截图
13.运行地址(使用花生壳进行内网穿透并配置端口,需本地开启tomcat才能远程访问)
14.扩展
(1) lombok的使用(编辑器需下载lombok插件,pom文件导入lombok依赖)
@Data //get和set
@NoArgsConstructor //无参构造方法
@AllArgsConstructor //有参构造方法
public class User implements Serializable{
private Integer id;
private String name;
private String gender;
private Integer age;
private String address;
private String qq;
private String email;
}
(2) mysql使用redis作缓存
1.准备工作:导入依赖,application.properties中配置redis,官网下载redis并且安装redis图形化界面工具,推荐下载RedisDesktopManager,并连接上redis
2.自定义配置类以及redis工具类(copy拿来用就行)
RedisConfig.java
@Configuration
@EnableCaching
public class RedisConfig<K, V> extends CachingConfigurerSupport {
/**
* 自定义缓存注解key的生成策略。默认的生成策略是看不懂的(乱码内容)
* 通过Spring 的依赖注入特性进行自定义的配置注入并且此类是一个配置类可以更多程度的自定义配置
*/
@Bean
@Override
public KeyGenerator keyGenerator() {
return (target, method, params) -> {
StringBuilder sb = new StringBuilder();
sb.append(target.getClass().getName());
sb.append(method.getName());
for (Object obj : params) {
sb.append(obj.toString());
}
return sb.toString();
};
}
/**
* 修改redisTemplate的序列化方式
* @param redisConnectionFactory
**/
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
//objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance ,
ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
// 序列号key value
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
/**
* 缓存配置管理器
* 解决cache(@Cacheable)把数据缓存到redis中的value是乱码问题
*/
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
// 解决查询缓存转换异常的问题
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
//om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance ,
ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
jackson2JsonRedisSerializer.setObjectMapper(om);
// 配置序列化(解决乱码的问题),过期时间600秒
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(600))
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
.disableCachingNullValues();
RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
.cacheDefaults(config)
.build();
return cacheManager;
}
}
RedisUtil.java(代码较长,不做展示)
3.在Service要使用redis缓存的方法中加入缓存注解
/**
* 查询所有
* @return
*/
@Cacheable(cacheNames = "allUser")
public List<User> findAll() {
System.out.println("正在使用mysql查找......");
return userDao.findAll();
}
4.测试
@Test
void contextLoads() {
List<User> users = userService.findAll();
System.out.println(users);
}
5.截图
再次调用该方法时会从redis中读取数据,不会通过mysql查找
redis缓存
(3) swagger的使用
导入swagger依赖,启动tomcat,访问http://localhost:端口/swagger-ui.html
@ResponseBody
@RequestMapping("/test")
public List<User> test(){
return userService.findAll();
}
可以看到接口返回的数据,如下图
15.优化
dao层可使用MybatisPlus,好处是不用自己写那些繁琐的sql,以及可以使用其提供的QueryWrapper条件构造器自定义语句,简化代码。前端页面可以使用Ajax发送异步请求,从而达到页面的局部刷新,体验感会更好。thymeleaf对比jsp+servlet的优点如下,thymeleaf是通过controller调取业务层处理数据并且返回数据给前台,前台通过th:action属性将数据提交给后台处理,因此只需一个controller即可,而jsp是通过servlet处理前台发送的数据,一般一个页面对应一个servlet,且获取表单的参数方式不同,servler是通过request以及表单name属性来获取值,可以获取map等,而thymeleaf只需在controller中传入参数与表单name属性对应即可,自动获取表单输入的值,方便操作。