基于SpringBoot4+Mybatis+Thymeleaf的用户管理系统开发实战

一.功能要求

1.管理员登录

2.进入主界面,记录用户名和上次登录时间,分页显示所有用户信息

3.单击【添加联系人】,能够添加用户信息。

【提交】 表格新增一个用户信息

4.【查询】能够筛选用户信息。

5.【修改】能够修改选中行用户信息。

6.【删除】能够删除选中行用户信息。

【删除选中】能够删除选中多行用户信息

二.实现的步骤

1.环境准备

  • JDK25

  • IDEA2026.1

  • Mysql8.0

  • Navicat Premium 16

  • apache-maven-3.9.15

  • apache-tomcat-11.0.21

2.创建数据库

复制代码
/*
 Navicat Premium Data Transfer

 Source Server         : mysql
 Source Server Type    : MySQL
 Source Server Version : 80045 (8.0.45)
 Source Host           : localhost:3306
 Source Schema         : userdb

 Target Server Type    : MySQL
 Target Server Version : 80045 (8.0.45)
 File Encoding         : 65001

 Date: 01/05/2026 17:31:45
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for admin
-- ----------------------------
DROP TABLE IF EXISTS `admin`;
CREATE TABLE `admin`  (
  `username` varchar(20) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL,
  `password` varchar(20) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL
) ENGINE = MyISAM AUTO_INCREMENT = 1 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = DYNAMIC;

-- ----------------------------
-- Records of admin
-- ----------------------------
INSERT INTO `admin` VALUES ('admin', '123');
INSERT INTO `admin` VALUES ('123456', '123456');

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(20) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL,
  `gender` varchar(5) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
  `age` int NULL DEFAULT NULL,
  `address` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
  `qq` varchar(20) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
  `email` varchar(50) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = MyISAM AUTO_INCREMENT = 59 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = DYNAMIC;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (10, '张三', '男', 31, '陕西', '12345', 'zhangsan@itcast.cn');
INSERT INTO `user` VALUES (15, '李四', '女', 15, '北京', '88888', 'ls@itcast.cn');
INSERT INTO `user` VALUES (16, '张三', '男', 13, '陕西', '12345', 'zhangsan@itcast.cn');
INSERT INTO `user` VALUES (17, '李四', '女', 15, '北京', '88888', 'ls@itcast.cn');
INSERT INTO `user` VALUES (18, '张三', '男', 13, '陕西', '123456', 'zhangsan@itcast.cn');
INSERT INTO `user` VALUES (19, '李四', '女', 15, '北京', '88888', 'ls@itcast.cn');
INSERT INTO `user` VALUES (56, '李四', '男', 1, '陕西', '1', 'ls@itcast.cn');
INSERT INTO `user` VALUES (58, 'PowerCodejt', '男', 18, '北京', '88888', 'One_Piece@163');
INSERT INTO `user` VALUES (57, '小草', '男', 18, '陕西', '88888', 'ls@itcast.cn');
INSERT INTO `user` VALUES (25, '李四', '女', 15, '北京', '88888', 'ls@itcast.cn');
INSERT INTO `user` VALUES (26, '张三', '男', 13, '陕西', '12345', 'zhangsan@itcast.cn');
INSERT INTO `user` VALUES (27, '李四', '女', 15, '北京', '88888', 'ls@itcast.cn');
INSERT INTO `user` VALUES (28, '张三', '男', 13, '陕西', '12345', 'zhangsan@itcast.cn');
INSERT INTO `user` VALUES (29, '李四', '女', 15, '北京', '88888', 'ls@itcast.cn');
INSERT INTO `user` VALUES (30, '张三', '男', 13, '陕西', '12345', 'zhangsan@itcast.cn');
INSERT INTO `user` VALUES (31, '李四', '女', 15, '北京', '88888', 'ls@itcast.cn');

SET FOREIGN_KEY_CHECKS = 1;

​

3.SpringBoot项目搭建

  • 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>4.0.6</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com</groupId>
        <artifactId>eUserSystem</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>eUserSystem</name>
        <description>eUserSystem</description>
        <url/>
    ​
        <properties>
            <java.version>25</java.version>
        </properties>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-thymeleaf</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-webmvc</artifactId>
            </dependency>
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>4.0.1</version>
            </dependency>
    ​
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-devtools</artifactId>
                <scope>runtime</scope>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>com.mysql</groupId>
                <artifactId>mysql-connector-j</artifactId>
                <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-thymeleaf-test</artifactId>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-webmvc-test</artifactId>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter-test</artifactId>
                <version>4.0.1</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid-spring-boot-starter</artifactId>
                <version>1.2.23</version>
            </dependency>
        </dependencies>
    ​
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <configuration>
                        <excludes>
                            <exclude>
                                <groupId>org.projectlombok</groupId>
                                <artifactId>lombok</artifactId>
                            </exclude>
                        </excludes>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <executions>
                        <execution>
                            <id>default-compile</id>
                            <phase>compile</phase>
                            <goals>
                                <goal>compile</goal>
                            </goals>
                            <configuration>
                                <annotationProcessorPaths>
                                    <path>
                                        <groupId>org.projectlombok</groupId>
                                        <artifactId>lombok</artifactId>
                                    </path>
                                </annotationProcessorPaths>
                            </configuration>
                        </execution>
                        <execution>
                            <id>default-testCompile</id>
                            <phase>test-compile</phase>
                            <goals>
                                <goal>testCompile</goal>
                            </goals>
                            <configuration>
                                <annotationProcessorPaths>
                                    <path>
                                        <groupId>org.projectlombok</groupId>
                                        <artifactId>lombok</artifactId>
                                    </path>
                                </annotationProcessorPaths>
                            </configuration>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    ​
    </project>

创建项目包结构

java 复制代码
src/main/
├── java/com/usersystem/
│   ├── UsersystemApplication.java
│   ├── config/
│   │   └── WebConfig.java
│   ├── controller/
│   │   ├── UserController.java
│   │   └── CheckCodeController.java
│   ├── domain/
│   │   ├── User.java
│   │   ├── Admin.java
│   │   └── PageBean.java
│   ├── interceptor/
│   │   └── LoginInterceptor.java
│   ├── mapper/
│   │   └── UserMapper.java
│   └── service/
│       ├── UserService.java
│       └── impl/UserServiceImpl.java
└── resources/
    ├── application.yml
    ├── mapper/
    │   └── UserMapper.xml
    ├── templates/
    │   ├── login.html
    │   ├── list.html
    │   ├── add.html
    │   └── update.html
    └── static/
        ├── css/
        ├── js/
        └── images/
  • 在static目录下引入bootstrap的css和js

  • application.yml

    复制代码
    ​
    server:
      port: 8080
    ​
    spring:
      datasource:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/userdb?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
        username: root
        password: 123456
        
        druid:
          initial-size: 5
          min-idle: 5
          max-active: 20
          max-wait: 60000
          
      thymeleaf:
        mode: HTML
        encoding: UTF-8
        cache: false
        
    mybatis:
      mapper-locations: classpath:mapper/*.xml
      type-aliases-package: com.eusersystem.domain
      configuration:
        map-underscore-to-camel-case: true
    ​
  • 启动类 - UsersystemApplication.java

复制代码
package com.eusersystem;
​
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
​
@SpringBootApplication
public class EUserSystemApplication {
​
    public static void main(String[] args) {
        SpringApplication.run(EUserSystemApplication.class, args);
    }
​
}
​
​
​
  • User.java
复制代码
package com.eusersystem.domain;
​
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
​
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private Integer id;
    private String name;
    private String gender;
    private Integer age;
    private String address;
    private String qq;
    private String email;
}
  • Admin.java
复制代码
​
package com.eusersystem.domain;
​
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
​
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Admin {
    private String username;
    private String password;
}
  • PageBean.java
复制代码
package com.eusersystem.domain;

import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;

import java.util.List;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class PageBean<T> {
    private int totalCount;
    private int totalPage;
    private List<T> list;
    private int currentPage;
    private int rows;
}
  • UserMapper.java

    复制代码
    package com.eusersystem.mapper;
    import com.eusersystem.domain.Admin;
    import com.eusersystem.domain.User;
    import org.apache.ibatis.annotations.Mapper;
    import org.apache.ibatis.annotations.Param;
    ​
    import java.util.List;
    import java.util.Map;
    ​
    @Mapper
    public interface UserMapper {
    ​
        List<User> findAll();
    ​
        Admin login(Admin admin);
    ​
        void addUser(User user);
    ​
        void deleteUser(Integer id);
    ​
        User findUserById(Integer id);
    ​
        void updateUser(User user);
    ​
        int findTotalCount(Map<String, String[]> condition);
    ​
        List<User> findByPage(@Param("start") int start,
                              @Param("rows") int rows,
                              @Param("condition") Map<String, String[]> condition);
    }
    ​
  • UserService.java

复制代码
​
package com.usersystem.service;
​
import com.usersystem.domain.Admin;
import com.usersystem.domain.PageBean;
import com.usersystem.domain.User;
​
import java.util.List;
import java.util.Map;
​
public interface UserService {
    List<User> findAll();
    Admin login(Admin admin);
    void addUser(User user);
    void deleteUser(String id);
    User findUserById(String id);
    void updateUser(User user);
    void delSelectedUser(String[] ids);
    PageBean<User> findUserByPage(String currentPage, String rows, Map<String, String[]> condition);
}
​
  • UserServiceImpl.java
复制代码
package com.eusersystem.servicee;
​
import com.eusersystem.domain.Admin;
import com.eusersystem.domain.PageBean;
import com.eusersystem.domain.User;
import com.eusersystem.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
​
import java.util.HashMap;
import java.util.List;
import java.util.Map;
​
@Service
public class UserServiceImpl implements UserService {
​
    @Autowired
    private UserMapper userMapper;
​
    @Override
    public List<User> findAll() {
        return userMapper.findAll();
    }
​
    @Override
    public Admin login(Admin admin) {
        return userMapper.login(admin);
    }
​
    @Override
    public void addUser(User user) {
        userMapper.addUser(user);
    }
​
    @Override
    public void deleteUser(String id) {
        userMapper.deleteUser(Integer.parseInt(id));
    }
​
    @Override
    public User findUserById(String id) {
        return userMapper.findUserById(Integer.parseInt(id));
    }
​
    @Override
    public void updateUser(User user) {
        userMapper.updateUser(user);
    }
​
    @Override
    public void delSelectedUser(String[] ids) {
        if (ids != null && ids.length > 0) {
            for (String id : ids) {
                userMapper.deleteUser(Integer.parseInt(id));
            }
        }
    }
​
    @Override
    public PageBean<User> findUserByPage(String currentPage, String rows, Map<String, String[]> condition) {
        int currPage = 1;
        if (currentPage != null && !currentPage.isEmpty()) {
            try {
                currPage = Integer.parseInt(currentPage);
                if (currPage < 1) currPage = 1;
            } catch (NumberFormatException e) {
                currPage = 1;
            }
        }
​
        int pageSize = 10;
        if (rows != null && !rows.isEmpty()) {
            try {
                pageSize = Integer.parseInt(rows);
                if (pageSize < 1) pageSize = 10;
            } catch (NumberFormatException e) {
                pageSize = 10;
            }
        }
​
        if (condition == null) {
            condition = new HashMap<>();
        }
​
        int totalCount = userMapper.findTotalCount(condition);
        
        int totalPage = totalCount / pageSize;
        if (totalCount % pageSize != 0) {
            totalPage++;
        }
        if (totalPage < 1) totalPage = 1;
​
        if (currPage > totalPage) {
            currPage = totalPage;
        }
​
        int start = (currPage - 1) * pageSize;
        List<User> list = userMapper.findByPage(start, pageSize, condition);
​
        PageBean<User> pb = new PageBean<>();
        pb.setCurrentPage(currPage);
        pb.setRows(pageSize);
        pb.setTotalCount(totalCount);
        pb.setTotalPage(totalPage);
        pb.setList(list);
​
        return pb;
    }
}
  • UserController.java
复制代码
package com.eusersystem.controller;
​
import com.eusersystem.domain.Admin;
import com.eusersystem.domain.PageBean;
import com.eusersystem.domain.User;
import com.eusersystem.servicee.UserService;
import jakarta.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
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.RequestParam;
​
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
​
@Controller
public class UserController {
​
    @Autowired
    private UserService userService;
​
    @GetMapping("/")
    public String index(HttpSession session) {
        if (session.getAttribute("loginUser") != null) {
            return "redirect:/user/list";
        }
        return "login";
    }
​
    @GetMapping("/login")
    public String loginPage() {
        return "login";
    }
​
    @PostMapping("/login")
    public String login(Admin admin, HttpSession session, Model model,
                        @RequestParam(required = false) String verifycode) {
​
        String checkcode = (String) session.getAttribute("CHECKCODE_SERVER");
        session.removeAttribute("CHECKCODE_SERVER");
​
        if (checkcode == null || verifycode == null || !checkcode.equalsIgnoreCase(verifycode)) {
            model.addAttribute("login_msg", "验证码错误!");
            return "login";
        }
​
        Admin loginUser = userService.login(admin);
        if (loginUser != null) {
            String lastLoginTime = (String) session.getAttribute("lastLoginTime");
            if (lastLoginTime != null) {
                session.setAttribute("lastLoginTime", lastLoginTime);
            }
            session.setAttribute("loginUser", loginUser);
            session.setAttribute("loginTime", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
                    .format(new Date()));
            return "redirect:/user/list";
        } else {
            model.addAttribute("login_msg", "用户名或密码错误!");
            return "login";
        }
    }
​
    @GetMapping("/user/list")
    public String list(HttpSession session, Model model,
                       @RequestParam(required = false) String currentPage,
                       @RequestParam(required = false) String rows,
                       @RequestParam(required = false) String name,
                       @RequestParam(required = false) String address,
                       @RequestParam(required = false) String email) {
​
        if (session.getAttribute("loginUser") == null) {
            return "redirect:/";
        }
​
        Map<String, String[]> condition = new HashMap<>();
        if (name != null && !name.isEmpty()) {
            condition.put("name", new String[]{name});
        }
        if (address != null && !address.isEmpty()) {
            condition.put("address", new String[]{address});
        }
        if (email != null && !email.isEmpty()) {
            condition.put("email", new String[]{email});
        }
​
        PageBean<User> pb = userService.findUserByPage(currentPage, rows, condition);
        model.addAttribute("pb", pb);
        model.addAttribute("condition", condition);
        model.addAttribute("lastLoginTime", session.getAttribute("lastLoginTime"));
​
        return "list";
    }
​
    @GetMapping("/user/add")
    public String addPage(HttpSession session) {
        if (session.getAttribute("loginUser") == null) {
            return "redirect:/";
        }
        return "add";
    }
​
    @PostMapping("/user/add")
    public String add(User user, HttpSession session) {
        userService.addUser(user);
        return "redirect:/user/list";
    }
​
    @GetMapping("/user/delete/{id}")
    public String delete(@PathVariable String id, HttpSession session) {
        userService.deleteUser(id);
        return "redirect:/user/list";
    }
​
    @GetMapping("/user/update/{id}")
    public String updatePage(@PathVariable String id, Model model, HttpSession session) {
        if (session.getAttribute("loginUser") == null) {
            return "redirect:/";
        }
        User user = userService.findUserById(id);
        model.addAttribute("user", user);
        return "update";
    }
​
    @PostMapping("/user/update")
    public String update(User user) {
        userService.updateUser(user);
        return "redirect:/user/list";
    }
​
    @PostMapping("/user/delSelected")
    public String delSelected(@RequestParam String[] uid) {
        userService.delSelectedUser(uid);
        return "redirect:/user/list";
    }
​
    @GetMapping("/logout")
    public String logout(HttpSession session) {
        session.invalidate();
        return "redirect:/";
    }
}
​
  • CheckCodeController.java
复制代码
package com.eusersystem.controller;
​
​
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
​
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;
​
@Controller
public class CheckCodeController {
​
    @GetMapping("/checkCode")
    public void checkCode(HttpServletResponse response, HttpSession session) throws IOException {
        int width = 100;
        int height = 50;
​
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics g = image.getGraphics();
​
        g.setColor(new Color(200, 200, 200));
        g.fillRect(0, 0, width, height);
​
        Random rand = new Random();
        g.setColor(new Color(100, 100, 100));
        for (int i = 0; i < 50; i++) {
            int x = rand.nextInt(width);
            int y = rand.nextInt(height);
            g.drawOval(x, y, 1, 1);
        }
​
        String code = "";
        g.setFont(new Font("Arial", Font.BOLD, 30));
        for (int i = 0; i < 4; i++) {
            String num = String.valueOf(rand.nextInt(10));
            code += num;
            g.setColor(new Color(rand.nextInt(150), rand.nextInt(150), rand.nextInt(150)));
            g.drawString(num, 20 * i + 10, 35);
        }
​
        session.setAttribute("CHECKCODE_SERVER", code);
​
        g.dispose();
        response.setContentType("image/jpeg");
        image.flush();
        javax.imageio.ImageIO.write(image, "jpeg", response.getOutputStream());
    }
}
​
  • LoginInterceptor.java
复制代码
package com.eusersystem.interceptor;
​
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
​
@Component
public class LoginInterceptor implements HandlerInterceptor {
​
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String uri = request.getRequestURI();
        if (uri.contains("/login") || uri.contains("/checkCode") || uri.equals("/")) {
            return true;
        }
​
        Object loginUser = request.getSession().getAttribute("loginUser");
        if (loginUser != null) {
            return true;
        }
​
        response.sendRedirect("/");
        return false;
    }
}
  • WebConfig.java
复制代码
package com.eusersystem.config;
​
​
import com.eusersystem.interceptor.LoginInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
​
@Configuration
public class WebConfig implements WebMvcConfigurer {
​
    @Autowired
    private LoginInterceptor loginInterceptor;
​
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/**")
                .excludePathPatterns("/", "/login", "/checkCode", "/css/**", "/js/**", "/images/**");
    }
}
​
  • UserMapper.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.eusersystem.mapper.UserMapper">
​
    <select id="findAll" resultType="User">
        SELECT * FROM user
    </select>
​
    <select id="login" parameterType="Admin" resultType="Admin">
        SELECT * FROM admin WHERE username = #{username} AND password = #{password}
    </select>
​
    <insert id="addUser" parameterType="User" useGeneratedKeys="true" keyProperty="id">
        INSERT INTO user (name, gender, age, address, qq, email)
        VALUES (#{name}, #{gender}, #{age}, #{address}, #{qq}, #{email})
    </insert>
​
    <delete id="deleteUser" parameterType="int">
        DELETE FROM user WHERE id = #{id}
    </delete>
​
    <select id="findUserById" parameterType="int" resultType="User">
        SELECT * FROM user WHERE id = #{id}
    </select>
​
    <update id="updateUser" parameterType="User">
        UPDATE user SET name=#{name}, gender=#{gender}, age=#{age},
        address=#{address}, qq=#{qq}, email=#{email} WHERE id=#{id}
    </update>
​
    <select id="findTotalCount" parameterType="map" resultType="int">
        SELECT COUNT(*) FROM user
        <where>
            <if test="condition['name'] != null and condition['name'][0] != null and condition['name'][0] != ''">
                AND name LIKE CONCAT('%', #{condition.name[0]}, '%')
            </if>
            <if test="condition['address'] != null and condition['address'][0] != null and condition['address'][0] != ''">
                AND address LIKE CONCAT('%', #{condition.address[0]}, '%')
            </if>
            <if test="condition['email'] != null and condition['email'][0] != null and condition['email'][0] != ''">
                AND email LIKE CONCAT('%', #{condition.email[0]}, '%')
            </if>
        </where>
    </select>
​
    <select id="findByPage" resultType="User">
        SELECT * FROM user
        <where>
            <if test="condition['name'] != null and condition['name'][0] != null and condition['name'][0] != ''">
                AND name LIKE CONCAT('%', #{condition.name[0]}, '%')
            </if>
            <if test="condition['address'] != null and condition['address'][0] != null and condition['address'][0] != ''">
                AND address LIKE CONCAT('%', #{condition.address[0]}, '%')
            </if>
            <if test="condition['email'] != null and condition['email'][0] != null and condition['email'][0] != ''">
                AND email LIKE CONCAT('%', #{condition.email[0]}, '%')
            </if>
        </where>
        LIMIT #{start}, #{rows}
    </select>
​
</mapper>
​
  • login.html
复制代码
​
<!DOCTYPE html>
<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8"/>
    <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
    <meta name="viewport" content="width=device-width, initial-scale=1"/>
    <title>用户管理系统 - 登录</title>
    <link href="/css/bootstrap.min.css" rel="stylesheet">
    <script src="/js/jquery-2.1.0.min.js"></script>
    <script src="/js/bootstrap.min.js"></script>
    <style>
            body {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            display: flex;
            align-items: flex-start;
            justify-content: center;
            padding-top: 15vh;
        }
        .login-container {
            background: white;
            padding: 40px;
            border-radius: 10px;
            box-shadow: 0 10px 25px rgba(0,0,0,0.2);
            width: 400px;
        }
        .login-header {
            text-align: center;
            margin-bottom: 30px;
        }
        .login-header h2 {
            color: #333;
            margin: 0;
        }
    </style>
</head>
<body>
    <div class="login-container">
        <div class="login-header">
            <h2>用户管理系统</h2>
        </div>
        
        <div th:if="${login_msg}" class="alert alert-danger" role="alert">
            <span th:text="${login_msg}"></span>
        </div>
        
        <form action="/login" method="post">
            <div class="form-group">
                <label for="username">用户名</label>
                <input type="text" class="form-control" id="username" name="username" placeholder="请输入用户名" required>
            </div>
            
            <div class="form-group">
                <label for="password">密码</label>
                <input type="password" class="form-control" id="password" name="password" placeholder="请输入密码" required>
            </div>
            
            <div class="form-group">
                <label for="verifycode">验证码</label>
                <div style="display: flex; gap: 10px;">
                    <input type="text" class="form-control" id="verifycode" name="verifycode" placeholder="请输入验证码" required style="flex: 1;">
                    <img src="/checkCode" οnclick="this.src='/checkCode?'+Math.random()" style="cursor: pointer; height: 34px;">
                </div>
            </div>
            
            <button type="submit" class="btn btn-primary btn-block">登录</button>
        </form>
    </div>
</body>
</html>
​
  • list.html
复制代码
​
<!DOCTYPE html>
<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>用户信息列表</title>
    <link href="/css/bootstrap.min.css" rel="stylesheet">
    <script src="/js/jquery-2.1.0.min.js"></script>
    <script src="/js/bootstrap.min.js"></script>
    <style>
        td, th { text-align: center; }
    </style>
</head>
<body>
<div class="container" style="margin-top: 20px;">
    <div style="text-align: right; margin-bottom: 10px;">
        <span>欢迎您,<span th:text="${session.loginUser.username}"></span> | 
              上次登录时间:<span th:text="${lastLoginTime != null ? lastLoginTime : '首次登录'}"></span> |
              <a href="/logout" style="color: #337ab7;">退出</a></span>
    </div>
    
    <h3 style="text-align: center">用户信息列表</h3>
    
    <div style="margin-bottom: 10px;">
        <div style="float: left;">
            <form class="form-inline" action="/user/list" method="get">
                <div class="form-group">
                    <label>姓名</label>
                    <input type="text" name="name" th:value="${condition['name'] != null ? condition['name'][0] : ''}" class="form-control">
                </div>
                <div class="form-group">
                    <label>籍贯</label>
                    <input type="text" name="address" th:value="${condition['address'] != null ? condition['address'][0] : ''}" class="form-control">
                </div>
                <div class="form-group">
                    <label>邮箱</label>
                    <input type="text" name="email" th:value="${condition['email'] != null ? condition['email'][0] : ''}" class="form-control">
                </div>
                <button type="submit" class="btn btn-default">查询</button>
            </form>
        </div>
        <div style="float: right;">
            <a class="btn btn-primary" href="/user/add">添加联系人</a>
            <a class="btn btn-primary" href="javascript:void(0);" id="delSelected">删除选中</a>
        </div>
        <div style="clear: both;"></div>
    </div>
    
    <form id="form" action="/user/delSelected" method="post">
        <table border="1" class="table table-bordered table-hover">
            <tr class="success">
                <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:each="user, stat : ${pb.list}">
                <td><input type="checkbox" name="uid" th:value="${user.id}"></td>
                <td th:text="${stat.count}"></td>
                <td th:text="${user.name}"></td>
                <td th:text="${user.gender}"></td>
                <td th:text="${user.age}"></td>
                <td th:text="${user.address}"></td>
                <td th:text="${user.qq}"></td>
                <td th:text="${user.email}"></td>
                <td>
                    <a class="btn btn-default btn-sm" th:href="@{/user/update/{id}(id=${user.id})}">修改</a>
                    <a class="btn btn-default btn-sm" href="javascript:void(0);" th:οnclick="'deleteUser(' + ${user.id} + ')'">删除</a>
                </td>
            </tr>
        </table>
    </form>
    
    <div style="text-align: center; margin-top: 20px;">
        <ul class="pagination" style="margin: 0; display: inline-flex;">
            <li th:class="${pb.currentPage == 1} ? 'disabled' : ''">
                <a th:href="@{/user/list(currentPage=${pb.currentPage - 1}, rows=${pb.rows}, 
                    name=${condition['name'] != null ? condition['name'][0] : ''},
                    address=${condition['address'] != null ? condition['address'][0] : ''},
                    email=${condition['email'] != null ? condition['email'][0] : ''})}"
                   aria-label="Previous">
                    <span aria-hidden="true">&laquo;</span>
                </a>
            </li>
            
            <li th:each="i : ${#numbers.sequence(1, pb.totalPage)}" 
                th:class="${pb.currentPage == i} ? 'active' : ''">
                <a th:href="@{/user/list(currentPage=${i}, rows=${pb.rows},
                    name=${condition['name'] != null ? condition['name'][0] : ''},
                    address=${condition['address'] != null ? condition['address'][0] : ''},
                    email=${condition['email'] != null ? condition['email'][0] : ''})}"
                   th:text="${i}"></a>
            </li>
            
            <li th:class="${pb.currentPage == pb.totalPage} ? 'disabled' : ''">
                <a th:href="@{/user/list(currentPage=${pb.currentPage + 1}, rows=${pb.rows},
                    name=${condition['name'] != null ? condition['name'][0] : ''},
                    address=${condition['address'] != null ? condition['address'][0] : ''},
                    email=${condition['email'] != null ? condition['email'][0] : ''})}"
                   aria-label="Next">
                    <span aria-hidden="true">&raquo;</span>
                </a>
            </li>
            
            <li style="margin-left: 15px;">
                <form class="form-inline" action="/user/list" method="get" style="display: inline;">
                    <label>每页显示:</label>
                    <select name="rows" class="form-control" οnchange="this.form.submit()">
                        <option value="5" th:selected="${pb.rows == 5}">5条</option>
                        <option value="10" th:selected="${pb.rows == 10}">10条</option>
                        <option value="15" th:selected="${pb.rows == 15}">15条</option>
                        <option value="20" th:selected="${pb.rows == 20}">20条</option>
                    </select>
                    <input type="hidden" name="currentPage" th:value="${pb.currentPage}">
                    <input type="hidden" name="name" th:value="${condition['name'] != null ? condition['name'][0] : ''}">
                    <input type="hidden" name="address" th:value="${condition['address'] != null ? condition['address'][0] : ''}">
                    <input type="hidden" name="email" th:value="${condition['email'] != null ? condition['email'][0] : ''}">
                </form>
            </li>
            
            <li style="margin-left: 15px;">
                <span>共<span th:text="${pb.totalCount}"></span>条记录,共<span th:text="${pb.totalPage}"></span>页</span>
            </li>
        </ul>
    </div>
</div>
​
<script>
    function deleteUser(id) {
        if (confirm("您确定要删除吗?")) {
            location.href = "/user/delete/" + id;
        }
    }
    
    $(function() {
        $("#delSelected").click(function() {
            if (confirm("您确定要删除选中条目吗?")) {
                var flag = false;
                $("input[name='uid']:checked").each(function() {
                    flag = true;
                });
                if (flag) {
                    $("#form").submit();
                }
            }
        });
        
        $("#firstCb").click(function() {
            $("input[name='uid']").prop("checked", this.checked);
        });
    });
</script>
</body>
</html>
​
  • add.html
复制代码
​
<!DOCTYPE html>
<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8">
    <title>添加用户</title>
    <link href="/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container" style="width: 600px; margin-top: 50px;">
    <h3 style="text-align: center">添加用户</h3>
    <form action="/user/add" method="post">
        <div class="form-group">
            <label>姓名</label>
            <input type="text" name="name" class="form-control" required>
        </div>
        <div class="form-group">
            <label>性别</label>
            <input type="radio" name="gender" value="男" checked>男
            <input type="radio" name="gender" value="女">女
        </div>
        <div class="form-group">
            <label>年龄</label>
            <input type="number" name="age" class="form-control" required>
        </div>
        <div class="form-group">
            <label>籍贯</label>
            <select class="form-control" id="addressSelect" οnchange="handleAddressChange()">
                <option value="">请选择或输入</option>
                <option value="北京">北京</option>
                <option value="上海">上海</option>
                <option value="广州">广州</option>
                <option value="深圳">深圳</option>
                <option value="杭州">杭州</option>
                <option value="成都">成都</option>
                <option value="武汉">武汉</option>
                <option value="南京">南京</option>
                <option value="西安">西安</option>
                <option value="重庆">重庆</option>
                <option value="其他">其他(手动输入)</option>
            </select>
            <input type="text" name="address" id="addressInput" class="form-control" style="margin-top: 10px; display: none;" placeholder="请输入籍贯">
        </div>
        <div class="form-group">
            <label>QQ</label>
            <input type="text" name="qq" class="form-control" required>
        </div>
        <div class="form-group">
            <label>邮箱</label>
            <input type="email" name="email" class="form-control" required>
        </div>
        <button type="submit" class="btn btn-primary">保存</button>
        <a href="/user/list" class="btn btn-default">返回</a>
    </form>
</div>
​
<script>
    function handleAddressChange() {
        var select = document.getElementById("addressSelect");
        var input = document.getElementById("addressInput");
        if (select.value === "其他") {
            select.style.display = "none";
            input.style.display = "block";
            input.required = true;
            input.focus();
        } else if (select.value !== "") {
            input.value = select.value;
        }
    }
</script>
</body>
</html>
​
  • update.html
复制代码
​
<!DOCTYPE html>
<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8">
    <title>修改用户</title>
    <link href="/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container" style="width: 600px; margin-top: 50px;">
    <h3 style="text-align: center">修改用户</h3>
    <form action="/user/update" method="post">
        <input type="hidden" name="id" th:value="${user.id}">
        <div class="form-group">
            <label>姓名</label>
            <input type="text" name="name" class="form-control" th:value="${user.name}" required>
        </div>
        <div class="form-group">
            <label>性别</label>
            <input type="radio" name="gender" value="男" th:checked="${user.gender == '男'}">男
            <input type="radio" name="gender" value="女" th:checked="${user.gender == '女'}">女
        </div>
        <div class="form-group">
            <label>年龄</label>
            <input type="number" name="age" class="form-control" th:value="${user.age}" required>
        </div>
        <div class="form-group">
            <label>籍贯</label>
            <input type="text" name="address" class="form-control" th:value="${user.address}" required>
        </div>
        <div class="form-group">
            <label>QQ</label>
            <input type="text" name="qq" class="form-control" th:value="${user.qq}" required>
        </div>
        <div class="form-group">
            <label>邮箱</label>
            <input type="email" name="email" class="form-control" th:value="${user.email}" required>
        </div>
        <button type="submit" class="btn btn-primary">保存</button>
        <a href="/user/list" class="btn btn-default">返回</a>
    </form>
</div>
</body>
</html>
​
相关推荐
鸟儿不吃草8 小时前
Android Java 自定义TextView点击取词,类似百度翻译的点击一段英文中的某个单词,可以显示点击了哪个单词
android·java·开发语言
麦麦大数据8 小时前
基于以太坊区块链+Spring Boot+Solidity智能合约的投票系统设计与实现
spring boot·后端·区块链·智能合约·投票系统
梦梦代码精8 小时前
LikeShop 是怎么解决数据库瓶颈的?
java·数据库·低代码·php
eRRA OFAG8 小时前
mysql之联合索引
java
薪火铺子8 小时前
CAS单点登录原理与实践
java·后端
知兀8 小时前
【微服务/nacos】Nacos注册中心原理;配置服务发现中间、配置中心
java·微服务·架构
一 乐8 小时前
茶叶商城|基于springboot + vue茶叶商城系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·论文·毕设·茶叶商城系统
AI进化营-智能译站8 小时前
ROS2 C++开发系列06:变量、数据类型与IO实战
java·开发语言·c++·ai
鱼弦8 小时前
Git 版本控制:Spring Boot 项目的分支管理与协作
spring boot