likes
comments
collection
share

基于SpringBoot、Freemarker 实现用户增删改查的完整案例

作者站长头像
站长
· 阅读数 15

环境准备

  • JDK 1.8 及以上
  • SpringBoot 2.5.5 及以上
  • MySQL 5.7 及以上
  • Maven
  • IntelliJ IDEA (可选)

创建项目

我们使用 IntelliJ IDEA 创建一个 Spring Boot Web 项目。

  1. 打开 IntelliJ IDEA,点击菜单栏的 "File",选择 "New",然后选择 "Project"。

  2. 在向导中选择 "Spring Initializr",然后点击 Next。

  3. 在 Spring Initializr 页面中,填写项目信息。

    • Group:自己定义
    • Artifact:自己定义
    • Dependencies:选择 "Spring Web"、"Spring Data JPA" 和 "MySQL Driver",然后点击 Next。
    • Metadata:自己定义

    然后点击 Finish,等待项目创建完成。

数据库配置

application.properties 文件中添加以下配置:

spring.datasource.url=jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.hibernate.ddl-auto=create

实现用户表

创建用户实体

我们先创建一个用户实体类 User,并使用 JPA 注解标记对应数据库的表名和字段名。

@Entity
@Table(name = "user")
public class User implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    private Integer age;

    @Column(name = "create_time")
    private LocalDateTime createTime;

    @Column(name = "update_time")
    private LocalDateTime updateTime;

    // Getter 和 Setter 略
}

创建用户 Repository

创建一个接口 UserRepository,并继承 JpaRepository 接口。

public interface UserRepository extends JpaRepository<User, Long> {
}

创建用户 Service

创建一个 Service 接口 UserService,定义对用户操作的方法。

public interface UserService {
    User save(User user);

    void deleteById(Long id);

    User findById(Long id);

    List<User> findAll();
}

创建一个 Service 实现类 UserServiceImpl,实现 Service 接口,对用户的增删改查操作进行具体实现。

@Service
public class UserServiceImpl implements UserService {

    private final UserRepository userRepository;

    @Autowired
    public UserServiceImpl(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    public User save(User user) {
        LocalDateTime now = LocalDateTime.now();
        user.setCreateTime(now);
        user.setUpdateTime(now);
        return userRepository.save(user);
    }

    @Override
    public void deleteById(Long id) {
        userRepository.deleteById(id);
    }

    @Override
    public User findById(Long id) {
        return userRepository.findById(id).orElse(null);
    }

    @Override
    public List<User> findAll() {
        return userRepository.findAll();
    }
}

实现 Controller

添加页面

我们需要先添加前端页面,这里使用 Freemarker 模板进行实现。

resources/templates/ 目录下创建名为 user.ftl 的文件,并填写以下代码:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>用户管理</title>
</head>
<body>
    <h2>用户管理</h2>

    <form>
        <input type="hidden" id="id" name="id"/>
        <br/>
        用户名:<input type="text" id="name" name="name" required="required"/>
        <br/>
        年龄:<input type="number" id="age" name="age" required="required"/>
        <br/><br/>

        <button type="button" id="save">保存</button>
    </form>

    <table>
        <tr>
            <th>id</th>
            <th>name</th>
            <th>age</th>
            <th>create_time</th>
            <th>update_time</th>
            <th>操作</th>
        </tr>
        <#list userList as user>
        <tr>
            <td>${user.id}</td>
            <td>${user.name}</td>
            <td>${user.age}</td>
            <td>${user.createTime}</td>
            <td>${user.updateTime}</td>
            <td>
                <button type="button" class="edit" data-id="${user.id}">编辑</button>
                <button type="button" class="delete" data-id="${user.id}">删除</button>
            </td>
        </tr>
        </#list>
    </table>

    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    <script>
        $(function() {
            function clearForm() {
                $('input[type="text"],input[type="number"]').val('');
                $('input[type="hidden"]').val(0);
            }

            function listUser() {
                $.get('/user', function (data) {
                    $('.user-table').replaceWith(data);
                });
            }

            $('form #save').click(function () {
                var id = $('form #id').val();
                var formData = $('form').serializeArray();
                var user = {};
                $.each(formData, function (_, kv) {
                    user[kv.name] = kv.value;
                });
                user.id = id;
                $.ajax('/user', {
                    data: JSON.stringify(user),
                    contentType: 'application/json',
                    type: (id === '0') ? 'POST' : 'PUT',
                    success: function () {
                        clearForm();
                        listUser();
                    }
                });
            });

            $(document).on('click', '.edit', function () {
                var id = $(this).data('id');
                $.get('/user/' + id, function (data) {
                    $.each(data, function (key, value) {
                        $('form #' + key).val(value);
                    });
                });
            });

            $(document).on('click', '.delete', function () {
                var id = $(this).data('id');
                $.ajax('/user/' + id, {
                    type: 'DELETE',
                    success: function () {
                        listUser();
                    }
                });
            });

            listUser();
        });
    </script>
</body>
</html>

创建 Controller

创建一个 Controller 类 UserController,并添加对用户增删改查的对应操作。同时,使用 @Controller@ResponseBody 注解实现对前端页面请求的响应,并使用 @Autowired 注解自动注入 UserService。

@Controller
@RequestMapping("/user")
public class UserController {

    private final UserService userService;

    @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping
    public String index(Model model) {
        List<User> userList = userService.findAll();
        model.addAttribute("userList", userList);
        return "user";
    }

    @PostMapping
    @ResponseBody
    public User add(@RequestBody User user) {
        return userService.save(user);
    }

    @PutMapping("/{id}")
    @ResponseBody
    public User update(@PathVariable("id") Long id, @RequestBody User user) {
        User oldUser = userService.findById(id);
        if (oldUser == null) {
            return null;
        }
        oldUser.setName(user.getName());
        oldUser.setAge(user.getAge());
        oldUser.setUpdateTime(LocalDateTime.now());
        return userService.save(oldUser);
    }

    @DeleteMapping("/{id}")
    @ResponseBody
    public void delete(@PathVariable("id") Long id) {
        userService.deleteById(id);
    }

    @GetMapping("/{id}")
    @ResponseBody
    public User find(@PathVariable("id") Long id) {
        return userService.findById(id);
    }
}

测试

启动应用程序,在浏览器中访问 http://localhost:8080/user,即可看到用户的增删改查界面。在该界面中,您可以添加、修改、删除和查询用户信息。同时,您也可以查看完整的工程目录结构。

工程目录结构

spring-boot-demo/
    ├─src/
    │  ├─main/
    │  │  ├─java/
    │  │  │  └─com/
    │  │  │      └─example/
    │  │  │          ├─controller/
    │  │  │          │  └─UserController.java
    │  │  │          ├─domain/
    │  │  │          │  └─User.java
    │  │  │          ├─repository/
    │  │  │          │  └─UserRepository.java
    │  │  │          ├─service/
    │  │  │          │  ├─impl/
    │  │  │          │  │  └─UserServiceImpl.java
    │  │  │          │  └─UserService.java
    │  │  │          └─SpringBootDemoApplication.java
    │  │  └─resources/
    │  │      ├─static/
    │  │      └─templates/
    │  │          └─user.ftl
    │  └─test/
    └─pom.xml

完整代码

为方便您的参考,这里提供一个完整的项目代码。其中,可以直接使用自己的数据库信息进行测试。

  1. User.java
package com.example.domain;

import javax.persistence.*;
import java.io.Serializable;
import java.time.LocalDateTime;

@Entity
@Table(name = "user")
public class User implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    private Integer age;

    @Column(name = "create_time")
    private LocalDateTime createTime;

    @Column(name = "update_time")
    private LocalDateTime updateTime;

    // Getter 和 Setter 略
}
  1. UserRepository.java
package com.example.repository;

import com.example.domain.User;
import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository<User, Long> {
}
  1. UserService.java
package com.example.service;

import com.example.domain.User;

import java.util.List;

public interface UserService {
    User save(User user);

    void deleteById(Long id);

    User findById(Long id);

    List<User> findAll();
}
  1. UserServiceImpl.java
package com.example.service.impl;

import com.example.domain.User;
import com.example.repository.UserRepository;
import com.example.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.List;

@Service
public class UserServiceImpl implements UserService {

    private final UserRepository userRepository;

    @Autowired
    public UserServiceImpl(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    public User save(User user) {
        LocalDateTime now = LocalDateTime.now();
        user.setCreateTime(now);
        user.setUpdateTime(now);
        return userRepository.save(user);
    }

    @Override
    public void deleteById(Long id) {
        userRepository.deleteById(id);
    }

    @Override
    public User findById(Long id) {
        return userRepository.findById(id).orElse(null);
    }

    @Override
    public List<User> findAll() {
        return userRepository.findAll();
    }
}
  1. UserController.java
package com.example.controller;

import com.example.domain.User;
import com.example.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDateTime;
import java.util.List;

@Controller
@RequestMapping("/user")
public class UserController {

    private final UserService userService;

    @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping
    public String index(Model model) {
        List<User> userList = userService.findAll();
        model.addAttribute("userList", userList);
        return "user";
    }

    @PostMapping
    @ResponseBody
    public User add(@RequestBody User user) {
        return userService.save(user);
    }

    @PutMapping("/{id}")
    @ResponseBody
    public User update(@PathVariable("id") Long id, @RequestBody User user) {
        User oldUser = userService.findById(id);
        if (oldUser == null) {
            return null;
        }
        oldUser.setName(user.getName());
        oldUser.setAge(user.getAge());
        oldUser.setUpdateTime(LocalDateTime.now());
        return userService.save(oldUser);
    }

    @DeleteMapping("/{id}")
    @ResponseBody
    public void delete(@PathVariable("id") Long id) {
        userService.deleteById(id);
    }

    @GetMapping("/{id}")
    @ResponseBody
    public User find(@PathVariable("id") Long id) {
        return userService.findById(id);
    }
}
  1. pom.xml
<dependencies>
    <!-- Spring Boot 数据 JPA 相关依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>

    <!-- Freemarker 模板引擎依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-freemarker</artifactId>
    </dependency>

    <!-- MySQL 数据库驱动依赖 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>

    <!-- Spring Boot Web 依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>
  1. SpringBootDemoApplication.java
package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringBootDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootDemoApplication.class, args);
    }
}
  1. application.properties
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=root

# 让 Hibernate 自动创建数据库表结构
spring.jpa.hibernate.ddl-auto=create
# 设置 Hibernate 方言
spring.jpa.database-platform=org.hibernate.dialect.MySQL8Dialect
  1. user.ftl
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>用户管理系统</title>
    <style>
        body {
            background-color: #F0F0F0;
        }

        .container {
            width: 1200px;
            margin: 20px auto;
            padding: 20px;
            border-radius: 5px;
            background-color: #FFFFFF;
            box-shadow: 0 0 5px #999999;
        }

        h1 {
            text-align: center;
            margin-bottom: 30px;
        }

        .add {
            margin-bottom: 20px;
        }

        .add input[type='text'], .add input[type='number'] {
            width: 150px;
            padding: 5px;
            border-radius: 5px;
            border: 1px solid #CCCCCC;
            margin-right: 10px;
        }

        .add button {
            padding: 5px 10px;
            border-radius: 5px;
            border: 1px solid #CCCCCC;
            background-color: #FFFFFF;
            cursor: pointer;
        }

        table {
            width: 100%;
            border-collapse: collapse;
            text-align: center;
            margin-bottom: 30px;
        }

        th {
            background-color: #F5F5F5;
            padding: 10px;
            border-bottom: 1px solid #CCCCCC;
        }

        td {
            padding: 10px;
            border-bottom: 1px solid #CCCCCC;
        }

        .btn-group {
            margin-bottom: 20px;
        }

        .btn-group button {
            padding: 5px 10px;
            border-radius: 5px;
            border: 1px solid #CCCCCC;
            background-color: #FFFFFF;
            cursor: pointer;
            margin-right: 10px;
        }
    </style>
</head>
<body>
<div class="container">
    <h1>用户管理系统</h1>

    <div class="add">
        <input type="text" id="name" placeholder="姓名">
        <input type="number" id="age" placeholder="年龄">
        <button onclick="addUser()">添加用户</button>
    </div>

    <table>
        <thead>
        <tr>
            <th>ID</th>
            <th>姓名</th>
            <th>年龄</th>
            <th>创建时间</th>
            <th>更新时间</th>
            <th>操作</th>
        </tr>
        </thead>
        <tbody>
        <#list userList as user>
        <tr>
            <td>${user.id}</td>
            <td>${user.name}</td>
            <td>${user.age}</td>
            <td>${user.createTime?string("yyyy-MM-dd HH:mm:ss")}</td>
            <td>${user.updateTime?string("yyyy-MM-dd HH:mm:ss")}</td>
            <td>
                <div class="btn-group">
                    <button onclick="editUser(${user.id})">编辑</button>
                    <button onclick="deleteUser(${user.id})">删除</button>
                </div>
            </td>
        </tr>
        </#list>
        </tbody>
    </table>
</div>

<script>
    function addUser() {
        let name = document.getElementById("name").value;
        let age = document.getElementById("age").value;
        let xhr = new XMLHttpRequest();
        xhr.open("POST", "/user");
        xhr.setRequestHeader("Content-Type", "application/json");
        xhr.onreadystatechange = function () {
            if (xhr.readyState === XMLHttpRequest.DONE) {
                if (xhr.status === 200 || xhr.status === 201) {
                    location.reload();
                } else {
                    alert("添加用户失败!");
                }
            }
        }
        xhr.send(JSON.stringify({name, age}));
    }

    function editUser(id) {
        let name = prompt("请输入姓名:");
        let age = prompt("请输入年龄:");
        let xhr = new XMLHttpRequest();
        xhr.open("PUT", `/user/${id}`);
        xhr.setRequestHeader("Content-Type", "application/json");
        xhr.onreadystatechange = function () {
            if (xhr.readyState === XMLHttpRequest.DONE) {
                if (xhr.status === 200 || xhr.status === 201) {
                    location.reload();
                } else {
                    alert("编辑用户失败!");
                }
            }
        }
        xhr.send(JSON.stringify({name, age}));
    }

    function deleteUser(id) {
        if (confirm("确定删除该用户吗?")) {
            let xhr = new XMLHttpRequest();
            xhr.open("DELETE", `/user/${id}`);
            xhr.onreadystatechange = function () {
                if (xhr.readyState === XMLHttpRequest.DONE) {
                    if (xhr.status === 200 || xhr.status === 201) {
                        location.reload();
                    } else {
                        alert("删除用户失败!");
                    }
                }
            }
            xhr.send();
        }
    }
</script>
</body>
</html>

总结

虽然现在前后端分离的场景比较普遍,前端有很多流行的框架,如Vue、Angular、React等,但是上面介绍的基于SpringBoot和Freemarker的用户管理系统对于初学者学习来说仍然是一份非常好的示例。

通过这个例子,我们可以学习到如何使用Spring Boot快速搭建一个Web应用,如何使用JPA进行数据持久化,以及如何使用Freemarker模板引擎来渲染页面。同时,这个例子也展示了一个最基本的CRUD应用,其中包括增加、删除、修改和查询用户。

无论是在学习过程中还是实际开发中,了解这些基本的概念和用法都是非常重要的。因此,我认为这个例子对于初学者来说是非常有用的。

相关文章源码放在:gitee仓库github仓库上。