使用SpringBoot一小时快速搭建一个简单后台管理(后端篇)

news2025/7/16 17:01:26

不好意思让大家久等啦,最近也是因为学期末了,事情多了一点,所以更新的比较慢,请大家谅解下~

好了话不多说,进入今天的教程环节

本次案例一共两篇文章教学:

(第一篇):数据表设计,前端框架引入和编写前端页面,搭建基本的springboot项目,引入前端到springboot项目中,在浏览器显示

(第二篇):后端代码的设计,这部分逻辑涉及的比较多,所以单独放一篇出来讲,代码从0手敲讲解,保证你能学会,完成增删改查的功能

各大技术基础教学、实战开发教学(最新更新时间2021-12-11)

效果演示

目录

前言

项目结构介绍

bean(数据层)

repository(数据访问层)

service(业务层接口)

serviceImpl(业务层实现)

controller(控制层)

页面加载显示数据

IndexController(页面加载显示所有用户)

前端修改

运行结果

查询功能模块

Ajax异步请求局部刷新

UserController处理查询请求

运行结果

删除功能模块

修改前端

UserController处理删除请求

运行结果

添加功能模块

JS和Ajax部分

UserController处理添加请求

修改功能模块

修改前端

JS逻辑处理

UserController处理修改请求

总结

Gitee开源项目下载地址(所以项目都在这里)

各大技术基础教学、实战项目开发教学


前言

我们在上一篇中呢已经简单的搭建了一个前端页面,有基础的增删改查按钮

同时呢也创建了一个SpringBoot项目,也做了一些初始配置,并且已经成功的将项目运行了起来,那么,我们今天就把后端完善,实现基本的增删改查功能

项目结构介绍

项目的整体结构目录如下

学过SSM的同学对这个目录结构是不是非常的熟悉,我们大致介绍一下每个目录的作用

bean数据层(存放数据类)
config存放一些基本配置文件
controller控制层(处理客户端的请求)
repository相当于DAO层(数据访问层,和数据库打交道)
service业务层的接口类(定义接口方法)
serviceImpl业务层的实现类(处理逻辑)

好了,整体项目结构以及每个目录的作用已经介绍完毕,接下来我们就开始设计吧~

bean(数据层)

我们在bean包下新建一个User类,添加两个注解

这里的@Entity注解里面为什么要加上name这个属性值呢,因为我们在上一篇中创建表的时候,保存表的表名为 t_user ,不知道大伙还记得没

如果不加上name这个属性,会默认类名小写(user)去识别数据库中的表,这时候就会对应不上,所以需要加上name属性与t_user表绑定

@Data注解会自动生成set,get,toString等方法

@GeneratedValue注解设置主键id为自增长,不然和数据库不一致会报错哦~

@Entity(name = "t_user")
@Data
public class User {

    @Id
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    private Integer id;
    private String username;
    private String nickname;
    private String password;
}

repository(数据访问层)

在repository包下新建一个UserRepository接口类(注意是接口哦!)

这里只需要定义接口类就行了,我们暂时还用不到自定义sql语句,所以其他不用写

public interface UserRepository extends JpaRepository<User, Integer> {

}

service(业务层接口)

在service包下新建一个UserService接口类(注意是接口哦!)

定义五个方法(增删改查),其中查询有两个方法,一个是页面加载要显示所有用户信息的,一个是通过搜索框搜索的模糊查询

public interface UserService {
    
    void insertUser(User user);// 添加用户
    
    void deleteUser(Integer uid);// 删除用户
    
    void updateUser(User user);// 修改用户
    
    List<User> selectAllUser();// 查询所有用户
    
    List<User> selectLike(String search);// 模糊查询
}

serviceImpl(业务层实现)

在serviceImpl包下新建一个UserServiceImpl类,实现UserService接口

前面四个基本的增删改查就不多说啦~大伙应该都知道

主要是第五个selectLike(重点!原创)这个方法,大伙可以仔细研究一下我的这个模糊查询设计模式,很有帮助的哦~ 这里不多介绍了,可以自己仔细学一学

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserRepository userRepository;

    @Override
    public void insertUser(User user) {
        userRepository.save(user);
    }

    @Override
    public void deleteUser(Integer uid) {
        userRepository.deleteById(uid);
    }

    @Override
    public void updateUser(User user) {
        userRepository.saveAndFlush(user);
    }

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

    /**
     * 查询优先级:
     * 1.先查询是否为整型,为整型则通过ID主键查询,返回结果,不为整型则模糊查询其他字段
     * 2.模糊查询字段,忽略密码的模糊查询,对用户名和昵称进行模糊查询,返回结果
     * @param search 查询字段
     * @return 查询列表集合
     */
    @Override
    public List<User> selectLike(String search) {
        List<User> list = new ArrayList<>();// 查询列表集合
        User user = new User();
        user.setUsername(search);
        user.setNickname(search);
        try {
            Integer uid = Integer.parseInt(search);
            Optional<User> optional = userRepository.findById(uid);
            if (!optional.isPresent()) {
                list = selectVague(user);
            } else {
                list.add(optional.get());
            }
        }catch (NumberFormatException e) {
            // 查询字段不为整型数据,捕获异常
            list = selectVague(user);
        }

        return list;
    }

    /**
     * 模糊查询
     * @param user
     * @return
     */
    private List<User> selectVague(User user) {
        List<User> list = null;
        ExampleMatcher matcher = ExampleMatcher.matchingAny()
                .withMatcher("username", ExampleMatcher.GenericPropertyMatchers.contains())
                .withMatcher("nickname", ExampleMatcher.GenericPropertyMatchers.contains())
                .withIgnoreCase("uid")
                .withIgnoreCase("password");
        Example<User> example = Example.of(user, matcher);
        list = userRepository.findAll(example);
        return list;
    }
}

controller(控制层)

基本的一个整体流程已经搭建完毕,最后就是处理controller控制层的逻辑,我们首先完善一下上一篇中的IndexController的代码,让页面加载时候就能显示所有用户的数据、

在controller包下新建一个UserController类,设置改控制类处理所有/user下的请求

@RestController
@RequestMapping(value = "/user")
public class UserController {

}

页面加载显示数据

IndexController(页面加载显示所有用户)

@RestController
public class IndexController {

    @Autowired
    private UserService userService;

    @RequestMapping(value = "/index") // 访问路径
    public ModelAndView toIndex() {
        // 返回templates目录下index.html
        ModelAndView view = new ModelAndView("index");
        // 查询所有的用户,添加到model视图里
        view.addObject("user_list", userService.selectAllUser());
        return view;
    }
}

前端修改

同时修改前端内容,通过th:each遍历user_list,分别获取编号,用户名,昵称,密码,通过th:text标签赋值text(对thymeleaf语法不熟悉的可以先去简单看一下基础语法哦~)

还有HTML顶部不要忘了添加支持thymeleaf语法哦~

运行结果

为了方便查看结果,预先在数据库插入几条数据

回到IDEA项目,点击右上角的debug模式启动项目,打开浏览器输入localhost:8081/index(路径根据自己情况来),就能看到数据已经成功显示上来啦



查询功能模块

Ajax异步请求局部刷新

给搜索按钮和搜索框都设置一个ID,同时给数据显示部分的div设置th:fragment标签,获取后端的代码片段

 在index.js中添加代码,向后端发送异步请求

// 查询
$('#findBtn').click(function () {
	// 发送GET异步请求
	$.ajax({
		type: 'GET',
		url: '/user/select', // 请求路径
		data: {
			'search': $('#search').val()
		},
		success: function (data) {
			// 局部刷新数据显示部分的div
			$('#userTable').html(data)
		},
		error: function (err) {
			console.log(err)
			alert('操作失败,请刷新重新尝试!')
		}
	})
})

UserController处理查询请求

@RestController
@RequestMapping(value = "/user")
public class UserController {

    // 注入业务层对象
    @Autowired
    private UserService userService;

    @GetMapping(value = "/select")
    public ModelAndView selectLike(String search) {
        // 返回th:fragment代码片段
        ModelAndView view = new ModelAndView("index::userTable");
        view.addObject("user_list", userService.selectLike(search));
        return view;
    }
}

运行结果

重新启动,输入xiao,成功显示

 输入数值,优先查询编号



删除功能模块

刚刚的查询功能前端呢我是在index.js里面处理的,接下来删除模块我们换一个方法

首先,删除一条数据,我们是不是通关编号id进行删除呀,所以发送请求的时候是不是要传递编号数据

修改前端

在删除按钮中添加一个th:onclick标签,传递参数为当前行的编号

 接着在html底部添加如下代码

<script th:inline="javascript">
	function deleteBtn(id) {
        // 发送请求
		$.ajax({
			type: 'POST',
			url: '/user/delete',
			data: {
				'id': 'id' // 编号
			},
			success: function (data) {
                // 局部刷新
				$('#userTable').html(data)
			},
			error: function (err) {
				console.log(err)
				alert("操作失败,请刷新重新尝试!")
			}
		})
	}
</script>

UserController处理删除请求

添加新的方法

@PostMapping(value = "/delete")
public ModelAndView deleteUser(Integer id) {
    // 通过编号删除用户
    userService.deleteUser(id);
    ModelAndView view = new ModelAndView("index::userTable");
    // 返回新的数据列表
    view.addObject("user_list", userService.selectAllUser());
    return view;
}

运行结果

点击删除第五条数据,成功删除了第五条数据并刷新表格 



添加功能模块

JS和Ajax部分

通过JQuery对提交的表单进行一些非空检验,通过之后发出POST异步请求

在index.js添加下面代码

// 添加
$('#addSubmitBtn').click(function () {
	var username = $('#addUserName').val()
	var nickname = $('#addNickname').val()
	var password = $("#addPassword").val()

	// 非空效验
	if (username.length == 0){
		alert("用户名不能为空")
	}else if(nickname.length == 0){
		alert("昵称不能为空")
	}else if (password.length == 0){
		alert("密码不能为空")
	}else {
		$.ajax({
			type: 'POST',
			url: '/user/insert',
			data: {
				'username': username, //用户名
				'nickname': nickname, //昵称
				'password': password //密码
			},
			success: function (data) {
				// 关闭modal框
				$('#modal-form-add').modal('hide')
				// 清空modal框里上一次的数据
				document.getElementById("addForm").reset()
				// 局部刷新
				$('#userTable').html(data)
			},
			error: function (err) {
				console.log(err)
				alert("操作失败,请刷新重新尝试!")
			}
		})
	}
})

UserController处理添加请求

添加新的方法

@PostMapping(value = "/insert")
public ModelAndView insertUser(User user) {
    // 插入数据
    userService.insertUser(user);
    // 回传代码片段
    ModelAndView view = new ModelAndView("index::userTable");
    view.addObject("user_list", userService.selectAllUser());
    return view;
}


修改功能模块

修改前端

当我们点击的修改的时候,弹出一个修改信息框,我们是不是要在信息框显示当前修改前的用户数据

这次我们使用th:onclick的方法快速绑定数据到弹出框,但是这次的数据有string字符串类型,之前的方法会报错,我们采用另一种新的方法

通过[[${ }]]方法传递字符串数据(那个报错小红点不用理,正常运行)

 接着在HTML底部的内联javascript里加上代码

// 点击修改按钮
function updateBtn(id, username, nickname, password) {
	// 传递数据到弹出框
	$('#modal-form-update').modal('show');
	$('#updateUserId').val(id)
	$('#updateUsername').val(username)
	$('#updateNickname').val(nickname)
	$('#updatePassword').val(password)
}

刷新浏览器,随便点击一个修改,成功显示数据上来了

同时给编号的input标签设置disabled属性,禁止修改

JS逻辑处理

在index.js添加如下代码

// 修改提交
$('#updateSubmitBtn').click(function () {
	var id = $('#updateUserId').val()
	var username = $('#updateUsername').val()
	var nickname = $('#updateNickname').val()
	var password = $("#updatePassword").val()

    // 非空检验
	if (username.length ==0) {
		alert('用户名不能为空')
	}else if (nickname.length == 0) {
		alert('昵称不能为空')
	}else if (password.length == 0) {
		alert('密码不能为空')
	}else {
		$.ajax({
			type: 'POST',
			url: '/user/update',
			data: {
				'id': id, //编号
				'username': username, //用户名
				'nickname': nickname, //昵称
				'password': password  //密码
			},
			success: function (data) {
				// 关闭modal框
				$('#modal-form-update').modal('hide')
				// 清空modal框里上一次的数据
				document.getElementById("updateForm").reset()
				// 局部刷新
				$('#userTable').html(data)
			},
			error: function (err) {
				console.log(err)
				alert('操作失败,请刷新重新尝试!')
			}
		})
	}
})

UserController处理修改请求

@PostMapping(value = "update")
public ModelAndView updateUser(User user) {
    userService.updateUser(user);
    ModelAndView view = new ModelAndView("index::userTable");
    view.addObject("user_list", userService.selectAllUser());
    return view;
}

总结

整体的一个项目呢已经做完了,增删改查的功能都有了

其实还有很多地方可以完善的,比如搜索框回车搜索、重复代码抽取成函数单独进行等等,这些有兴趣的小伙伴自己再去优化啦~ 或者私信我帮忙也行~

这里总结我再帮大家捋一捋整个流程

  • 表的设计和创建
  • 搭建前端静态页面
  • 搭建后端
    • bean数据层
    • repository数据访问层
    • service业务接口
    • serviceImpl业务实现
    • controller控制层
  • 优化前端(JS,Ajax)

Gitee开源项目下载地址(所以项目都在这里)

SpringBoot项目教学合集: CSDN中的所有SpringBoot项目开源,持续更新新项目、新教学文章

各大技术基础教学、实战项目开发教学

各大技术基础教学、实战开发教学(最新更新时间2021-12-11)

没有Gitee账号可以后台CSDN私聊我获取项目源码,或者文章留言你的邮箱我也会发给你

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/406497.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

聊聊vue3的defineProps、defineEmits、defineExpose

最近在开发中用到了vue3的defineProps、defineEmits和defineExpose&#xff0c;感觉发现新大陆一般&#xff0c;所以利用闲碎时间对这三个方法做个总结。 defineProps const props defineProps<{foo: String,bar?: Number }>()defineProps 是vue3的写法并且是一个仅 …

Video.js 使用教程 - 手把手教你基于 Vue 搭建 HTML 5 视频播放器

本文首发&#xff1a;《Video.js 使用教程 - 手把手教你基于 Vue 搭建 HTML 5 视频播放器》 Video.js 是最强大的网页嵌入式 HTML 5 视频播放器的组件库之一&#xff0c;也是大多数人首选的网页视频播放解决方案。复杂的网页视频渲染&#xff0c;在引入 Video.js 后&#xff0…

idea连接SQL Server数据库

数据库配置 1.安装数据库&#xff1a;自行安装 我的SQL Server版本为2019 2.登录数据库&#xff0c;登陆方式有两种&#xff0c;连接数据库选择SQLServer身份验证 1.windows登录&#xff0c;直接登录 2.SQLServer登录&#xff0c;需要输入用户名和密码&#xff0c;默…

【Vant Weapp】van-cell 单元格

目录 自定义内容​​​​​​​ 自定义右侧value&#xff08;姓名手机号&#xff09; 自定义右侧value&#xff08;文件预览&#xff09; 自定义下方label描述信息 真机border相当明显 修改样式&#xff08;下边框、文字&#xff09; 循环列表中的cell 自定义内容 自定义…

前端必学 - 大文件上传如何实现

前端必学 - 大文件上传如何实现写在前面问题分析开始操作一、文件如何切片二、得到原文件的hash值三、文件上传四、文件合并技术点总结【重要】一、上传文件&#xff1f;二、显示进度三、暂停上传四、Hash有优化空间吗&#xff1f;五、限制请求个数六、拥塞控制&#xff0c;动态…

Vue+element ui实现好看的个人中心

目录一、效果图二、项目结构三、界面效果和代码实现1.路由注册2.个人主页实现3.编辑弹窗按钮实现4.个人简介实现5.发贴页实现6.收藏页实现7.关注和收藏页实现四、总结一、效果图 仿照原神社区的个人中心写了个个人中心界面&#xff0c;下图分别为原神社区个人中心主页和我画的…

vue项目天地图使用

最近的项目中遇到了新的需求&#xff0c;需要在项目中使用天地图&#xff0c;因为第一次接触&#xff0c;官方的网站引用之类的也没有进行详细的介绍&#xff0c;自己去找的时候发现这部分的文章也比较少&#xff0c;有的问题也没有讲清楚&#xff0c;所以发布这篇文章分享总结…

关于将tomcat卸载干净

这学期我们开始学习Java Web技术&#xff0c;要求安装tomcat&#xff0c;我到官网上下载的时候不小心下载了最新的测试版&#xff0c;但是安装的eclipse无法配置最新班的tomcat&#xff0c;就开启了我的下载、卸载之旅&#x1f62d;&#x1f62d; 在此之前也有在网上找了很多相…

小程序怎么自定义导航栏,导航栏放图片、设置高度

今天来说一下小程序的自定义导航栏。 1、设置导航栏style为custom&#xff1a; 2、这是刷新页面&#xff0c;页面的内容就跑到了页面的顶端&#xff0c;不留丝毫间隙&#xff1a; 3、然后定义一个components&#xff0c;就是我们自定义的导航栏组件&#xff1a; &#xff…

Vue3 + Element Plus 按需引入 - 自动导入

文章目录1 前言1.1 目的1.2 最终效果2 准备工作3 按需引入3.1 安装插件3.2 修改 vite.config.ts 文件4 其他4.1 ElMessageBox 使用时报错4.1.1 Eslint 报错&#xff1a; ElMessageBox is not defined.eslint(no-undef)4.1.2 TS 报错&#xff1a; Cannot find name ElMessageBox…

html设置背景颜色以及背景图片

背景颜色 backgroud-color:transparent color transparent : 背景色透明 color : 指定背景颜色 直接设置标签的style属性&#xff08;行内样式&#xff09; 例&#xff1a;将这个段落的背景设为红色 用选择器进行设置&#xff08;内嵌样式、外链样式&#xff0…

做技术,最忌讳东张西望

又好长时间没更新&#xff0c;研二了&#xff0c;忙着做实验、写论文、发论文&#xff0c;再加上给我导做一些事情&#xff08;都习惯了&#xff0c;以前很不爽的事情&#xff0c;现在居然能这么平静的说出来&#xff09;。 但这不是我今天说的重点&#xff0c;而是另外一件事…

3 分钟掌握 Node.js 版本的区别

在我们日常开发中&#xff0c;Node.js 使用场景越来越多&#xff0c;大到服务端项目&#xff0c;小到开发工具脚本&#xff0c;所以掌握 Node.js 一些基础知识是非常有必要的。 今天主要聊一下 Node.js 中 LTS 和 Current 的区别和如何选择合适的版本。 一、版本介绍 在官网上…

vue使用jsMind(思维导图)

前言 jsMind 是一个显示/编辑思维导图的纯 javascript 类库&#xff0c;其基于 html5 的 canvas 进行设计。 我们使用它可能需要在网页上单纯的使用这种图样的效果&#xff0c;而其他交互却是自定义的&#xff0c;我这边选择的是jsMind 与 网上的一个jsmind.menu.js&#xff…

Node.js 全网最详细教程 (第一章:Node学习入门必看教程)

1&#xff1a;Node的学前必知&#xff1a; 1: 在学习node之前&#xff0c;想必你应该学习过HTML&#xff0c;CSS&#xff0c;JavaScript 2: 浏览器中的JavaScript由两部分组成&#xff1a;JS核心语法和WebAPI JS核心语法WebAPI变量&#xff0c;数据类型DOM操作循环&#xff0…

Nginx静态资源部署

目录 Nginx静态资源概述 Nginx静态资源的配置指令 listen指令 server_name指令 location指令 设置请求资源的目录root / alias index指令 error_page指令 静态资源优化配置语法 Nginx静态资源压缩实战 Gzip模块配置指令 Gzip压缩功能的实例配置 Gzip和sendfil…

geoserver发布地图服务

geoserver发布地图服务发布wmts服务发布样式发布映像服务发布要素服务发布wmts服务 新建工作空间 保存后点击工作区 将shp文件上传到服务器 发布geoserver 服务 选择数据存储-》添加新的数据存储 这时可以选择两种方式 一种是直接将整个shp文件导入&#xff0c;一种是一…

【TS】object类型

object是一个对象&#xff0c;在ts中定义对象类型的语法为&#xff1a;let 变量名 &#xff1a;object { } 在object类型中&#xff0c;对象内部定义的值是不受类型约束的&#xff0c;只要是一个object类型即可&#xff0c;例如&#xff1a; let obj : object {name : 艺术概…

HTML <span>标签

HTML 中的<span>标签被视为内联元素。它类似于 div 标记&#xff0c;但 div 标记特意用于块级元素&#xff0c;而 span 用于内联元素。它主要用于用户想要将内联元素分组到其代码结构中。HTML 中的 Span 标记用于通过使用元素类或 id 属性为特定内容提供样式。使用 HTML …

element-ui table使用type=‘selection‘复选框全禁用-全选禁用

目录 问题总结&#xff1a; 当条件数据全被禁用时&#xff0c;全选按钮也变成禁用的状态&#xff0c;而不是隐藏。有会做的小伙伴希望跟帖。谢谢&#xff01; 复选框框架&#xff1a;通过调用selectable方法&#xff0c;进行禁用复选框。 1.指定行禁用&#xff1a; 2.条件禁用&…