本文详细介绍了一个支持增删查改的、前后端分离且运行过程中支持数据异步提交,页面完全不刷新的Java Web通讯录,内附全部代码,只要严格按照教程一定能够跑通!
本项目基于MVC框架主要展开,MVC即Model-View-Controller(模型-视图-控制器)架构。View层就是用户交互页面,它负责从Controller层获取系统中的各项数据并进行统一展示,对于这些数据的处理方法、调取逻辑等没有直接关联,不需要关心其实现方式,只是对其加以呈现。Controller层则是整个系统的资源和数据控制中心,它会接收来自于View层的用户操作请求,并调取Model层中相关的Service方法完成业务处理并响应给View层,而Service的业务设计则主要依靠Dao层对于数据库的处理进行完成。
本项目的前后端分离主要体现在后端Controller层只专注于对通讯录数据的调用和处理,当需要向前端输出数据时仅输出json格式的数据。前后端通信主要依靠ajax通过url进行json数据相应和传递参数。后端不需要考虑前端如何向用户展示数据,前端也不需要考虑后端时如何进行的数据处理。
本项目使用的具体技术栈包括:Java 17、SpringBoot、JSP、Maven、MySQL8.0.31、MyBatis、jQuery、Bootstrap、Ajax、模态框;前后端通信格式为json格式;软硬件环境及IDE为MacBook Air M2+macOS Ventura 13.1+IntelliJ IDEA 2022+Navicat Premium 16.0.7+Safari 16.5.2。
话不多说,上教程和代码!代码中含有大量注释,记录了大部分在运行时遇到的坑。
目录
一、准备工作
1.配置Tomcat服务器
见链接🔗:Mac OS配置Tomcat服务器教程
2.配置Maven环境
见链接🔗:Mac OS配置Maven环境教程(IntelliJ IDEA)
3.安装MySQL服务器并连接数据库GUI管理工具
首先配置MySQL数据库,教程见链接🔗:Mac OS安装配置MySQL 8.0.31教程,然后连接一个数据库GUI管理工具,笔者使用的是Navicat Premium 16.0.7,也可以使用如Sequel Ace、DBeaver等平替,当然也可以使用MySQL命令行直接操作。以下默认安装了Navicat。
打开Navicat,点击左上角连接,然后选择MySQL。
自定义一个连接名,输入MySQL密码。
然后点击左下角的“测试连接”,提示连接成功,点击“好”并点击“保存”。至此,Navicat Premium成功连接本地MySQL。
二、数据库准备
本项目以最简单的通讯录进行,通讯录内包括【姓名】和【联系方式】两部分,除此之外还需要一个数据库内部ID用于精准定位和方便增减数据。
在本地MySQL数据库下新建一个数据库命名为address_book,右击Local MySQL,点击“新建数据库”,在数据库名处输入“address_book”,然后点击“好”。
双击数据库address_book将其打开,然后在该数据库下右击“表”点击新建表(或command+N快捷新建),新建一个数据表(table),在表中新建三个字段,分别为ID、Name和Phone_number,字段类型、长度、null和主键设置如下图,注意ID要勾选自动递增,小窗左上角点击保存,并命名为address_book_data,点击“保存”。
如下图所示,在address_book数据库下能够找到address_book_data数据表即可,项目的所有通讯录数据将存储于该table中,此时数据库准备完成。
三、项目开发
1.新建SpringBoot工程
打开IDEA,新建Project。
左侧选择Spring Initalizr,给自己的项目命名,Type选择Maven,JDK选择17(其他版本的Java JDK也可以),Packaging选择War,然后Next。
选择Dependencies,分别为Web中的Spring Web和SQL中的MySQL Driver和MyBatis Framework,然后Create。
新建Project完成后,项目结构如图(忽略里面的Demox,笔者实际使用的项目名称为Demo3)。
2.工程结构
需要新建的主要为两部分
a.后端
com.example.demo3目录下新建四个新Package目录。
bean目录下新建Java Class为User;
controller目录下新建Java Class为UserController;
mapper目录下新建Interface为UserMapper;
service目录下新建Java Class为UserService。
b.前端
main目录下,与java和resources平级新建Package为webapp,然后在webapp下新建Directory为WEB-INF,然后在WEB-INF下新建jsp文件index.jsp。由于本项目实现了所有数据全部通过Ajax异步刷新,所以Web页面不需要刷新,只需要一个jsp文件。
完成后,项目最终结构如下:
3.代码
Talk is cheap!
a.后端代码
User.java
package com.example.demo3.bean;
// model层的实体类,数据访问层
public class User {
private int ID;
private String Name;
private String Phone_number;
public int getID(){
return ID;
}
public void setID(int ID){
this.ID = ID;
}
public String getName() {
return Name;
}
public void setName(String Name) {
this.Name = Name;
}
public String getPhone_number() {
return Phone_number;
}
public void setPhone_number(String Phone_number) {
this.Phone_number = Phone_number;
}
}
UserController.java
package com.example.demo3.controller;
import com.example.demo3.bean.User;
import com.example.demo3.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.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
// @RestController和@Controller都是Spring框架中用来标识一个类为控制器的注解。
// @RestController注解是Spring4.0版本引入的新特性,它的作用是将该类下的所有方法的返回值都默认为JSON格式的数据。这意味着在使用@RestController
// 注解标注的类中所有方法的返回值都会被自动转换为JSON格式并返回给客户端。而@Controller注解则是Spring MVC框架中的一个基本注解,它的作用是标识一个
// 类为控制器并且该类中的方法通常用来处理HTTP请求和响应。在使用@Controller注解的类中,通常需要配合使用其他注解来实现请求参数绑定、视图渲染等功能
// 比如@RequestMapping、@RequestParam、@ModelAttribute等。
// @RestController是@Controller和@ResponseBody的组合注解
@Controller
// @Controller是一个特殊的@Component,用于标识一个类为控制器层的组件,通常用于接收请求,处理请求参数,调用Service层提供的服务,返回响应结果。
// 控制器Controller层
public class UserController {
@Autowired
private UserService userService;
// @RequestMapping是一个通用的注解,可以用于处理任何类型的HTTP请求,包括GET、POST、PUT、DELETE等。它可以用于类级别和方法级别,用于指定请求的URL路径和请求方法。
// @GetMapping是@RequestMapping的一个特殊化版本,用于处理HTTP GET请求。它只能用于方法级别,用于指定请求的URL路径。相比于@RequestMapping,它更加简洁明了,也更加易于使用。
// 总的来说,如果只需要处理HTTP GET请求,建议使用@GetMapping;如果需要处理其他类型的HTTP请求,可以使用@RequestMapping。
// 注意,这里对于注解的使用会直接影响到view层中ajax对于post和get的选择,对应错了是跑不通的!
@RequestMapping("/index")
public String Index(){
return "index";
}
// 指定首页为index.jsp
@RequestMapping("test")
@ResponseBody
// @ResponseBody注解用于将Controller的方法返回的对象,通过适当的转换器转换为指定的格式之后,写入到response对象的body区,通常用来返回JSON数据或者是XML数据,一般在异步获取数据时使用
public List<User> getUserList(){
return userService.getUserList();
}// 上面这个方法简单将数据库中的所有数据全部输出,在项目中没有实际使用,主要用来帮助理解和debug
// 用于查找
@RequestMapping("select")
@ResponseBody
public List<User> selectUser(String Name){
return userService.selectUser(Name);
}// 返回json格式的结果
// 用于添加
@RequestMapping("add")
@ResponseBody
public void addUser(String Name,String Phone_number){
userService.addUser(Name,Phone_number);
}
// 用于删除
@RequestMapping("del")
@ResponseBody
public void delUser(int ID){
userService.delUser(ID);
}
// 用于更改
@RequestMapping("update")
@ResponseBody
public void updateUser(int ID,String Name,String Phone_number){
userService.updateUser(ID,Name,Phone_number);
}
}
UserMapper.java
package com.example.demo3.mapper;
import com.example.demo3.bean.User;
import org.apache.ibatis.annotations.*;//用于在MyBatis框架中定义Mapper接口
import java.util.List;
@Mapper
// @mapper 是一种数据映射模式,用于将对象与数据库表之间的映射关系定义。它可以帮助程序员简化对数据库的操作,使用对象而不是原始 SQL 语句来操作数据库。
// 帮助开发者能够使用注解或者xml文件通过sql语句直接操作数据库
public interface UserMapper {
//数据持久层,用于存放sql语句,在SpringBoot中用注解来为每一个方法注入sql语句,也叫Dao层
@Select("select * from address_book_data")
List<User> getUserList();
// 查询数据库中对应的所有数据
@Select("select * from address_book_data where Name like concat('%',#{Name},'%')")
List<User> selectUser(String Name);
//根据Name进行模糊查询
@Insert("insert into address_book_data(Name,Phone_number)values(#{Name},#{Phone_number})")
void addUser(String Name,String Phone_number);
// 向数据库中添加新的数据,主要包括Name和Phone_number,而ID在数据库中作为主键已经设置为自增
@Delete("delete from address_book_data where ID =#{ID}")
void delUser(int ID);
// 根据ID将数据库中的数据删除
@Update("update address_book_data set Name =#{Name}, Phone_number =#{Phone_number} where ID =#{ID}")
void updateUser(int ID, String Name, String Phone_number);
// 更新对应的数据
}
UserService.java
package com.example.demo3.service;
import com.example.demo3.bean.User;
import com.example.demo3.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
// @Component是一个通用的注解,用于标识一个类为Spring容器中的组件,可以被其他组件依赖注入。
// @Service是一个特殊的@Component,用于标识一个类为业务逻辑层的组件,通常用于封装业务逻辑,提供给Controller层调用。
// 业务逻辑层,用于完成功能设计,一般用于调用dao层的接口,实现业务功能
public class UserService {
@Autowired
private UserMapper userMapper;
// 以下五个业务功能分别对应Mapper中的五个数据操作
public List<User> getUserList(){
return userMapper.getUserList();
}
public List<User> selectUser(String Name){
return userMapper.selectUser(Name);
}
public void addUser(String Name,String Phone_number){
userMapper.addUser(Name,Phone_number);
}
public void delUser(int ID){
userMapper.delUser(ID);
}
public void updateUser(int ID,String Name,String Phone_number){
userMapper.updateUser(ID,Name,Phone_number);
}
}
b.前端代码
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>通讯录</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<%--使用jquery和bootstrap框架,需要添加下面的依赖包--%>
<script src="webjars/jquery/3.4.1/jquery.min.js"></script>
<script src="webjars/bootstrap/3.3.5/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="webjars/bootstrap/3.3.5/css/bootstrap.min.css" />
<style type="text/css">
h1 {
text-align: center;
}
body {
background-color: antiquewhite;
}
th, td {
width: 70px;
height: 35px;
text-align: center;
}
#before {
text-align: center;
}
</style>
<%--主要用于显示查找到的数据--%>
<script type="text/javascript">
function selList(Name){
$.ajax({
url:"http://localhost:8080/select?Name="+Name,
// 通过url进行传参
type:"post",
dataType:"json",
success:function (data){
var i;
var Userlist = "<thead><tr bgcolor=\"#5f9ea0\"><th>姓名</th><th>电话</th><th>选项</th></tr></thead>";
// 把获取到的从后端传来的json格式的数据用for循环拆解
for(i = 0; i < data.length; i++){
Userlist += "<tbody><tr><td>" + data[i].name + "</td><td>" + data[i].phone_number + "</td><td>"+
"<div class=\"btn-group\"><button type=\"button\" class=\"btn btn-info btn-sm\" οnclick=editUser('"+
data[i].id + "','" + data[i].name + "','" + data[i].phone_number+ "')>编辑</button>"+
"<button type=\"button\" class=\"btn btn-danger btn-sm\" οnclick=\"delUser("+
data[i].id + ")\">删除</button></div></td></tr></tbody>"
}//在查询数据显示的同时,也在对应的数据后方添加了修改和删除的按钮
//$("#Listtab").append(Userlist)
//上面这行给Userlist设置table标签id为"Listtab"的代码不能使用,会造成ajax刷新时无法清空上一次查询的数据,造成数据无限堆叠非常离谱
document.getElementById("Listtab").innerHTML = Userlist;
},
error: function(msg){
alert("数据查询异常:"+msg);
}
});
}
</script>
<%--该函数在上面的编辑按钮处使用,用户在按下编辑按钮后--%>
<script type="text/javascript">
function editUser(ID,Name,Phone_number) {
$("#editModal").modal('show');// 显示模态框
$("#editName").val(Name);// 在模态框中显示对应数据的Name元素
$("#editPhone_number").val(Phone_number);// 在模态框中显示对应数据的Phone_number元素
$("#editbutton").attr("value",ID);// 将editbutton的value属性设为ID,方便在修改数据时能够对应数据的主键ID
}
</script>
<%--该函数用于修改数据--%>
<script type="text/javascript">
function updateUser(ID){
var Name = document.getElementById("editName");
var Phone_number = document.getElementById("editPhone_number");
$.ajax({
url: "http://localhost:8080/update?ID="+ID+"&Name="+Name.value+"&Phone_number="+Phone_number.value,
type: "post",
//dataType: "json",
// 后端在修改完成后没有实际传回参数
success: function (){
alert("数据已修改");
$('#editModal').modal('hide')
selList("");
},
error:function(msg){
alert("修改失败:"+msg);
}
});
// 仅更新数据的function,Controller不会给前端返回任何数据,但是对于接受Ajax请求的Controller方法,不管是否需要返回数据
// 都需要在返回值前面加上@ResponseBody注解,或者说通过Response手动回写一段数据,例如:“OK”,否则ajax的回调函数将无法执行
}
</script>
<%--该函数用于删除数据--%>
<script type="text/javascript">
function delUser(ID) {
$.ajax({
url: "http://localhost:8080/del?ID="+ID,
type: "post",
success: function (){
alert("联系人已删除");
selList("");
},
error:function (msg){
alert("删除失败:"+msg);
}
});
}
</script>
<%--该函数用于添加数据--%>
<script type="text/javascript">
function addUser() {
var Name = document.getElementById("Name");
var Phone_number = document.getElementById("Phone_number");
$.ajax({
url: "http://localhost:8080/add?Name="+Name.value+"&Phone_number="+Phone_number.value,
type: "post",
success: function (){
alert("数据已提交");
$('#addModal').modal('hide')
selList("");
},
error:function (msg){
alert("添加失败:"+msg);
}
});
}
</script>
</head>
<body>
<%--大标题--%>
<h1><b>通讯录</b></h1>
<hr/>
<%--用于数据查找的文本输入框--%>
<div id="before">
<p><input type="text" id="txt1" placeholder="输入姓名进行查找" onkeyup="selList(this.value)"></p>
</div>
<%--在第一次打开的初始页面即以空参数对数据库进行一次查询,起到显示全部数据的效果--%>
<table class="table" align="center" id="Listtab" cellspacing="5">
<script type="text/javascript">
selList("");
</script>
</table>
<%--按钮:添加数据,打开id为addModal的模态框--%>
<div style="text-align:center">
<button class="btn btn-default" data-toggle="modal" data-target="#addModal">添加数据</button>
</div>
<%--模态框(addModal)--%>
<div class="modal fade" id="addModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">
×
</button>
<h4 class="modal-title" id="myModalLabel">
添加新的数据至通讯录
</h4>
</div>
<div class="modal-body">
<div>
<tr>
<label>姓名:</label></td>
<input type="text" name="Name" id="Name" placeholder="姓名">
</tr>
</div>
<div>
<tr>
<label>电话:</label>
<input type="text" name="Phone_number" id="Phone_number" placeholder="电话">
</tr>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" onclick="addUser()">添加</button>
<%--在添加按钮处运行addUser()函数--%>
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
</div>
</div>
</div>
</div>
<%--模态框(editModal)--%>
<div class="modal fade" id="editModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">
×
</button>
<h4 class="modal-title" id="editModalLabel">
更新通讯录
</h4>
</div>
<div class="modal-body">
<div>
<tr>
<label>姓名:</label></td>
<input type="text" name="Name" id="editName" placeholder="姓名">
</tr>
</div>
<div>
<tr>
<label>电话:</label>
<input type="text" name="Phone_number" id="editPhone_number" placeholder="电话">
</tr>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" id="editbutton" onclick="updateUser(value)">修改</button>
<%--在修改按钮处运行updateUser()函数--%>
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
</div>
</div>
</div>
</div>
</body>
</html>
c.配置文件
application.properties
# 数据库连接,注意此处的url和password需要修改成你自己对应的地址和MySQL密码
spring.datasource.driver-class-name = com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/address_book?serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=00000000
# 指定视图文件的前缀
spring.mvc.view.prefix=/WEB-INF/
# 指定视图的后缀
spring.mvc.view.suffix=.jsp
d.项目依赖
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>3.1.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>Demo3</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>Demo3</name>
<description>Demo3</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.2</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter-test</artifactId>
<version>3.0.2</version>
<scope>test</scope>
</dependency>
<!--用于编译jsp-->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<!--用于导入jquery框架的依赖-->
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.4.1</version>
</dependency>
<!--用于导入bootstrap框架的依赖-->
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap</artifactId>
<version>3.3.5</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
4.项目启动
很简单的两步,首先在IDE右侧工具栏打开Maven,点击Maven工具左上角的刷新按钮Reload All Maven Projects,将项目所需的依赖全部下载。然后IDE右上角绿色三角形一键运行!
四、项目运行
启动后,在Safari浏览器地址栏中输入http://localhost:8080,即可打开Web页面。
顶部查找框支持不区分大小写的姓名实时模糊查询,如输入“y”后将实时显示Sky与Tonyo的数据。
删除操作就不多解释了,编辑和添加数据均采用了模态框进行操作,简洁美观易操作。点击“编辑”后,弹出对应模态框,可供用户直接修改。
点击“添加数据”按钮同理。
至此,本项目结束。快打开你的电脑试试吧!看在笔者这么肝的份儿上有用的话留个赞👍吧🥺🥺🥺。
五、补充
最后提一句,由于前后端的彻底分离,本项目将很方便地在保持后端不变的前提下将前端JSP页面迁移至Vue框架,实现更加轻量化的前端开发。链接🔗在这里:支持增删查改的进阶版Java Web通讯录超详细教程【Vue2+SpringBoot+MySQL+Axios,前后端完全分离,附全部代码】