1、准备知识:
1)需要掌握的知识:
Java基础,JavaWeb开发基础,Spring基础(没有Spring的基础也可以,接触过Spring最好),ajax,Jquery,Mybatis。
2)项目结构介绍:
-
crud-core:这里面存放的是项目的一些公共的东西,如工具类util包、自定义异常类、枚举类型等。
-
crud-sys:这里面主要是和数据库操作有关系的文件。实体类entity、在没有使用Mybatis情况下有repository或dao实体操作类、使用Mybatis情况下有Mapper类、Service类和Service的实现类都在sys子模块下。
-
实体类entity:是对应于数据库的一些实体类,是所有数据操作的基础。
-
repository或dao:是对实体类进行增删改查操作的类,是直接和数据库进行交互的类,这两个使用哪个都可以,完全看个人爱好。
-
Mapper:是Mybatis下与数据库进行交互实现实体类的操作,和repository和dao相似。
-
Service:是在前端Controller和后端Dao层之间进行协调的类,用于接收Controller传递过来的信息,并调用Dao层对数据库进行操作后返回信息给Controller。
-
crud-web:是整个项目中唯一一个和外部网页交互的模块。包含Controller,启动类等。
-
Controller:接收用户从前端发送的request请求,指定请求的路径以及返回信息给前台页面。
-
pom.xml文件:每个项目都有自己的pom.xml文件,里面包含项目和父项目的信息,以及项目所依赖的所有依赖文件,在pom.xml文件中写入依赖后,项目会自动从本地仓库中查找需要的jar包,如果没有则从远程仓库中下载后存入本地仓库,这样就省去了下载jar包的步骤。
-
application.properties文件:这里面写着项目中的所有配置,包括数据库的连接、mybatis的配置,自定义配置项、项目端口等等各种配置。
下面开始实现一个简单的学生的添加
2、创建实体类
在crud-sys子项目中创建entity文件夹并创建和数据库相对应的Student类及其get,set函数。
public class Student { /** 学生id */ private String Name; /** 学生英语成绩 */ private Integer English; /** 学生数学成绩 */ private Integer Math; /** 学生计算机成绩 */ private Integer Computer; public String getName () { return Name; } public void setId(String Name) { this.Name = Name; } public Integer getEnglish() { return English; } public void setEnglish(Integer english) { English = english; } public Integer getMath() { return Math; } public void setMath(Integer math) { Math = math; } public Integer getComputer() { return Computer; } public void setComputer(Integer computer) { Computer = computer; } }
3、集成Mybatis
这个项目中继承了Mybatis,所以在进行数据库操作之前我们需要进行Mybatis的相关配置以及数据库的连接操作。
1)查找依赖包
进入https://mvnrepository.com/网站,输入mybatis查询mybatis依赖包:
这里选择org.mybatis.spring.boot里的mybatis-spring-boot-starter:
点击之后选择版本,这里我不习惯选择太新的也不喜欢太旧的版本,所以一般会在稍微新的版本中选择用户数最多的那一个,比如mybatis我选择2.0.1
点击进去之后复制依赖:
2)注入依赖并下载jar包
第一步之后,将文本复制到crud-web子项目的pom.xml文件中,在
此时右下角出现Import changes和Enable auto import,这里可以选择后者,以后修改pom.xml文件后,项目会自动导入jar包。
3)连接数据库
首先还是要通过上面的方法添加mysql的依赖到pom.xml文件中
<dependency> <groupId>mysqlgroupId> <artifactId>mysql-connector-javaartifactId> <version>8.0.15version> dependency>
在crud-web的resource文件夹下的application.properties文件中配置连接信息:
这些和普通的web开发中的DBUtil的写法大同小异。
4)配置mybatis
同样在application.properties文件配置mybatis的映射文件路径
mybatis.mapper-locations=classpath:/mapping/*.xml
这句话的意思是mybatis的映射文件存在于classpath(classpath就是生成target目录后classes文件所在目录)的mapping目录下以.xml结尾的文件。
4、编写Mapper类
1)创建文件
在crud-sys子项目下新建mapper文件夹,其下新建StudentMapper接口:
-
这里要注意,在Mapper接口上需要加入@Compent注解,表示这是一个和Bean类似的实体。
-
@Component:泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。
2)编写函数
Mapper类中主要写对实体的操作函数的定义,这里我们先定义一个添加函数。
添加代码:public void add(Student student);
5、编写映射文件Mapping文件
1)创建文件
在resource文件夹下创建mapping文件夹,在其下创建StudentMapping.xml文件。
2)添加xml头
在xml文件中写入mybatis映射文件需要的头,可以在网上搜索mybatis映射文件头文件,也可进入官网去找
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">
3)设置所映射的接口
设置这个xml文件对应的是哪个接口类中的函数。
添加
4)编写相应的函数所对应的SQL
Mybatis提供了insert、update、delete和select分别对应添加、更改、删除和查询操作,这里我们用到的是insert标签。
<insert id="add" parameterType="com.dingcheng365.crudsys.entity.Student ">
insert>
-
parameterType:参数类型,如果没有参数或两个以上参数,可不写改属性,如果是一个参数,需要写上参数的类型,最好将包名加上,如String类型写java.lang.String,Map类型写java.util.Map
-
resultType:这个在这个例子中没有体现。表示的是函数的返回值类型,如果为void可不写该属性。这里需要注意的是:resultType表示每一个元素的值,例如我们要进行查询,返回值是List
,因为查询结果不一定是一个学生满足条件,但是这时候resultType需要填写的是Student类而非List。
如果字段多的话添加语句会很长很难写,这里介绍一个快速且简单的办法:
在Navicat中随便选中一行,右键复制为insert语句:
粘贴后效果:
INSERT INTO `test`.`student` (`Name`, `English`, `Math`, `Computer`) VALUES ('zhangsan', '69', '86', '77');
这时候只需要修改后面的参数即可。
将语句粘贴到insert标签中,同时修改参数,使用#{}中间加上参数的名称:
6、编写Service层
1)编写service接口类
在crud-sys下创建service文件夹,在其下创建StudentService接口类,里面写入函数,一般会和Mapper类中的函数相似,可能会返回值不同。
2)编写service实现类
在service文件夹下创建impl文件夹,在其下创建StudentServiceImpl类实现StudentSerivce接口。
这里需要注意的是:Service实现类上面需要加入@Service注解。在这个类中需要使用@Autowired注解注入studentMapper对象。
-
@Autowired:自动导入依赖的bean
-
@Service:一般用于修饰service层的组件
然后编写代码,调用studentMapper的添加函数,添加成功后返回1,如果失败则返回0;
@Autowired private StudentMapper studentMapper; @Override public int add(Student student) { try { studentMapper.add(student); return 1; } catch (Exception e) { return 0; } }
至此,crud-sys子项目中所需要实现的功能已经编写完毕,接下来就是和网页的交互了。
7、编写页面
SpringBoot可以识别出resources文件夹下的static及其子文件夹下的文件,如果需要自定义文件夹,则需要在配置文件中配置。
1)创建文件并写入元素
在curd-web子目录的templates文件夹下创建addstudent.html并写入表单元素。
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>学生添加title>
head>
<body>
<form id="studentinfo">
<table>
<tr>
<td>姓名:td>
<td><input type="text" name="name" id="name">td>
tr>
<tr>
<td>英语成绩:td>
<td><input type="text" name="English" id="English">td>
tr>
<tr>
<td>数学成绩:td>
<td><input type="text" name="Math" id="Math">td>
tr>
<tr>
<td>计算机成绩:td>
<td><input type="text" name="Computer" id="Computer">td>
tr>
<tr>
<td colspan="2">
<button id="add-student-btn" type="button">提交button>
td>
tr>
table>
form>
body>
html>
2)表单验证
使用jquery-validate对表单输入的值进行验证,导入jquery.min.js和jquery-validate.min.js后进行编写。
// 表单验证 userInfoValidate = $('#userInfo').validate({ rules: { name: { required: true }, English: { required: true }, Math: { required: true }, Computer: { required: true } }, messages: { name: { required: '姓名不能为空' }, English: { required: '英语成绩不能为空' }, Math: { required: '数学成绩不能为空' }, Computer: { required: '计算机成绩不能为空' } }, errorElement: 'small', errorPlacement: function(error, element) { // 在error 上添加 `invalid-feedback` class error.addClass('invalid-feedback'); element.parent().append(error); }, highlight: function(element) { $(element) .addClass('is-invalid') .removeClass('is-valid'); }, unhighlight: function(element, errorClass, validClass) { var $valid = $(element); if (!validClass) { $valid.removeClass('is-invalid is-valid'); } else { $valid.addClass('is-valid').removeClass('is-invalid'); } }, submitHandler: function (form) { // 确认登录时删除存储标签页对象 // TODO: KEY值请参考`site-configs.js`文件中配置的`tabSittings`的值 window.sessionStorage.removeItem('admui.contentTabs'); } });
-
通过id调用validate函数,示例中的$('#userinfo').validate({..})
-
rules:表示表单验证的规则,rules中的第一层的子元素对应的是input标签的name而非id值。
-
子元素验证,在子元素中写入需要进行的验证,常用的有required:true表示不可为空,false表示可为空;minlength:设置输入的最短长度;maxlength:输入的最长长度;email:true表示必须输入正确的电子邮件;digtis:true表示必须输入整数等。
-
message:自定义提示信息,与rules中一一对应,表示不满足规则时提示的信息。
-
errorElement:用什么标签标记错误,默认是label
-
errorPlacement:自定义错误放置的位置
-
highlight:可以给未通过验证的元素加效果、闪烁等。
-
submitHandler:所有验证通过后执行的函数
效果图:
3)Ajax实现和Controller的交互
$("#add-student-btn").on('click', function () { $.ajax({ url: '/student/addstudent',//请求的地址 type: 'post',//请求类型:GET、POST、DELETE、PUT data: { //向请求传递的数据 "name": $("#name").val(), "English": $("#English").val(), "Math": $("#Math").val(), "Computer": $("#Computer").val() }, async: true, //是否异步,默认为true success: function (e) { //请求成功的回调函数,e为后台传递过来的信息 console.log(e) }, error: function (e) { //请求失败的回调函数 console.log("ajax请求失败"); } }); });
通过提交按钮的点击事件,向student/addstudent发送POST请求,传递的数据为用户输入的信息,请求后将后台返回的信息打印到控制台。
8、编写Controller层
Controller是与前台进行直接交互的模块,所以在crud-web子项目中创建包controller,在其下创建StudentController类。
-
Controller类都需要加入注解@Controller,表示这是一个控制类
-
Controller类上面的@RequestMapping中的参数表示这个Controller中的所有映射路径都是以这个参数开头
-
如果Controller中一个函数需要返回json数据类型,则需要加入@ResponseBody注解,通常用来返回JSON数据或者是XML,一般我们返回字符串类型时也需要使用这个注解
-
慎用@RestController,@RestController是@ResponseBody和@Controller的结合,当你返回的类型不是json等的时候,使用这个会将你返回的数据自动转换成json类型返回。比如你需要返回一个页面的时候就不能使用这个。
-
Controller返回页面也是一个很重要的点,可以参考我的博客:https://www.cnblogs.com/guo-xu/p/11203740.html
-
这里会发现一个错误:service无法进行注入。原因是service所在的路径必须是启动类所在的路径的子目录。修改目录结构后问题得到解决。
@Controller @RequestMapping("/student") public class StudentController { @Autowired private StudentService studentService; @RequestMapping("/addstudent") public String addstudent(Student student) { return String.valueOf(studentService.add(student)); } }
由于前台传递数据的时候所传递的json对象与Student对象的属性一一对应,所以在Controller接收的时候可以直接在参数中书写Student对象,系统会进行自动转换。
9、控制显示页面
我们需要编写IndexController来实现默认页面的显示,在controller文件夹下创建IndexController文件,写入根路径的映射:
@RequestMapping("/") public ModelAndView index() { return new ModelAndView("addstudent.html"); }
如果发现设置完毕后启动项目显示404的问题,可以参考博客:https://www.cnblogs.com/guo-xu/p/11203740.html里面详细阐述了SpringBoot如何返回页面的问题
10、启动项目
在CrudWebApplication.java下右键Run CrudWebApplication,发现启动错误:
这个问题产生的原因是虽然我们在Mapper类上面都加入了@Compent注解,但是项目并不知道有Mapper,不知道需要去加载Mapper,所以我们需要在启动类上方加入@MapperScan注解,参数为basePackages = {"com.dingcheng365.crud.sys.mapper"},加入之后在项目启动的时候就会自动扫描这个包中的所有Mapper类。
@MapperScan(basePackages = {"com.dingcheng365.crud.sys.mapper"})
重新启动项目:
出现这个提示表示项目启动成功,端口号为8080,如果想修改项目的端口号,可以修改application.properties文件加入 server.port=端口号 配置项。
11、测试
启动后输入localhost:8080,由于我们之前配置了根路径映射的函数,是返回addstudent.html页面,所以会出现这个页面。
输入信息后点击提交,发现添加失败,查看控制台报错:
这个问题的解决办法可参考博客:https://www.cnblogs.com/guo-xu/p/11177028.html
最简单的解决办法就是在连接语句中加入serverTimezone=GMT
加入之后重新启动并输入信息后点击提交,发现数据库中存在新添加的信息,至此学生信息添加的功能实现完毕。
实现了这个功能之后,删除、修改、查询之类的功能基本大同小异,这里我只说几点需要注意的地方。
12、其他问题
1)删除时删除单个信息
如果删除单个信息我们一般会使用/delete/id这样的路径,也就是直接将id值传递到路径中,并且不使用?id=1这种格式,这样在Controller类中接收的时候有所不同,下面是实例:
@RequestMapping("/deletestudent/{id}") @ResponseBody public String deleteUser(@PathVariable("id") String id) { return studentService.delete(id); }
可以看到,需要加入@PathVariable注解,参数为需要读取的值的名称,而且在RequestMapping中写的路径id值不确定需要加上大括号。
2)删除时删除多个信息(批量删除)
批量删除的时候,我们在前台传递的肯定是一个数组,里面包含要删除的多个id值,这个在Controller接受的时候也有所不同,由于是数组的问题。下面是实例:
前台:
$.ajax({ url: "/user/deleteUsers", type: "post", data: { ids": ids }, success: function (e) { if (e == "success") { toastr.success("删除成功"); } else { toastr.error("删除失败,请稍后重试"); } }, error: function (e) { console.log(e); } });
后台接收:
@RequestMapping(value = "/deleteUsers") @ResponseBody public String deleteUsers(@RequestParam("ids[]") Listids,) { …… }
在进行数组接受的时候,需要在@RequestParam注解中加入名称加上[]的参数才能正确接收。
3)前台传递过来的对象并没有和后台相对应
这个实例中,我们从前台接收的对象的参数正好和后台存在的Student类相对应,所以我们在后台直接用Student对象接受,那如果不一致的时候该怎么办?有两种情况:
- 参数少的时候
当参数个数比较少,2-4个左右时,我们可以直接在接收的函数中一个一个罗列出来,对应关系使用@RequestParam来进行区分。
- 参数多的时候
当参数个数比较多时,我个人推荐一个类型——Map类,这个类真的是百用不爽,由于他和json结构上的相似性,导致这个类的用途非常之大。在进行数据查询的时候如果我们要查询的结果并不是某一个类的集合,这个时候用Map对象也可以很完美的解决问题。