SSM整合项目
文章目录
- SSM整合项目
-
- 一、项目简介
-
- 1.项目简介
- 2.主要实现的功能
- 3.涉及的技术
- 4.最终效果图
- 二、搭建环境
-
- 1.创建Maven工程,添加依赖
- 2.再Maven下部署web工程
- 3.在resources目录中配置框架的配置文件
-
- 1.SpringMVC的配置文件
- 2.Spring的配置文件
- 3.jdbc信息 jdbc.properties
- 4.MyBatis的配置文件
- 5.MyBatis逆向工程的配置
- 6.创建数据库表
- 4.完成逆向工程创建
- 三、查询功能实现
-
- 1.需要实现的功能
- 2.利用zui前端框架快速搭建index.html页面
- 3.修改EmpMapper接口
- 4.修改EmpMapper.xml文件
- 5.添加 Msg 类引入链式编程
- 5.编写service层的方法
- 6.编写Controller层的方法
- 效果图:
- 四、新增功能的实现
-
- 1.需要实现的功能
- 2.编写service层的方法
- 3.编写Controller层的方法
- 4.index.html界面(部分)
- 效果图:
- 五、修改数据的实现
-
- 1.需要实现的功能
- 2.编写Service层的方法
- 3.编写Controller层的方法
- 4.index.html界面(部分)
- 效果图
- 六、删除数据的实现
-
- 1.需要实现的功能
- 2.编写Service层的方法
- 3.编写Controller层的方法
- 4.index.html界面(部分)
- 效果图:
- 项目总结
[GitHub项目地址:]https://github.com/sundaybody/SSM-study.git
SSM整合项目
一、项目简介
1.项目简介
使用ssm框架搭建出一套简单的CRUD的项目,主要涉及员工表和部门表,实现员工表的增删改查,其中每个员工对应这一个部门,属于多对一的关系,部门表和员工表属于多对一的关系
2.主要实现的功能
1.分页展示所有员工的基本信息
2.实现添加员工和删除员工
3.实现修改员工信息
4.实现批量删除员工信息
5.实现员工添加和修改时的数据校验工作
▪jQuery前端校验用户名和邮箱是否合法
▪Ajax请求校验用户名是否重复
▪JSR303后端检验用户名、邮箱是否合法以及用户名是否重复
3.涉及的技术
1.后端框架:Spring5+SpringMVC+MyBatis3
2.前端技术:zui开源HTML5框架,html5
3.视图渲染技术:thymeleaf
4.MyBatis分页插件:PageHelper
5.MyBatis逆向工程:mybatis-generator
6.RESTfui风格url
7.数据库:MySql 5.7+c3p0数据库连接池技术
4.最终效果图
二、搭建环境
本次项目的环境:
idea 2021.2.1
Tomcat 8.0.24
Maven 3.6.3
jdk8
其他依赖版本见pom.xml文件
1.创建Maven工程,添加依赖
pom.xml文件 注意:打包方式为war
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.wang.ssm</groupId> <artifactId>ssmbuild2.0</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.9</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.3.9</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.3.9</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.3.9</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.6</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.2</version> </dependency> <dependency> <groupId>com.mchange</groupId> <artifactId>c3p0</artifactId> <version>0.9.5.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.27</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf-spring5</artifactId> <version>3.0.12.RELEASE</version> </dependency> <dependency> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-core</artifactId> <version>1.3.5</version> </dependency> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.2.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.12.1</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>6.0.17.Final</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-maven-plugin</artifactId> <version>1.3.0</version> <dependencies> <dependency> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-core</artifactId> <version>1.3.5</version> </dependency> <dependency> <groupId>com.mchange</groupId> <artifactId>c3p0</artifactId> <version>0.9.5.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.27</version> </dependency> </dependencies> </plugin> </plugins> </build></project>
2.再Maven下部署web工程
在/src/main 下创建webapp目录配置web.xml文件
web.xml 整合Spring和SpringMVC
字符编码过滤器 ,一定要放在所有过滤器之前
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>DispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:SpringMVC.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>DispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <filter> <filter-name>CharacterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>utf-8</param-value> </init-param> <init-param> <param-name>forceResponseEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter> <filter-name>HiddenHttpMethodFilter</filter-name> <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter </filter-class> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter> <filter-name>FormContentFilter</filter-name> <filter-class>org.springframework.web.filter.FormContentFilter</filter-class> </filter> <filter-mapping> <filter-name>FormContentFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping></web-app
3.在resources目录中配置框架的配置文件
1.SpringMVC的配置文件
主要负责处理前端发送的请求,配置Thymeleaf视图解析器
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:mvc="http://www.springframework.org/schema/mvc"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd"> <context:component-scan base-package="com.wang.ssm"></context:component-scan> <bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver"> <property name="order" value="1"/> <property name="characterEncoding" value="UTF-8"/> <property name="templateEngine"> <bean class="org.thymeleaf.spring5.SpringTemplateEngine"> <property name="templateResolver"> <bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver"> <property name="prefix" value="/WEB-INF/templates/"/> <property name="suffix" value=".html"/> <property name="templateMode" value="HTML5"/> <property name="characterEncoding" value="UTF-8" /> </bean> </property> </bean> </property> </bean> <mvc:view-controller path="/" view-name="index"></mvc:view-controller> <mvc:default-servlet-handler/> <mvc:annotation-driven/></beans>
2.Spring的配置文件
applicationContext.xml 主要配置和业务逻辑有关的以及和MyBatis的整合
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <context:component-scan base-package="com.wang"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> <context:property-placeholder location="classpath:jdbc.properties"/> <bean id="pooledDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property> <property name="driverClass" value="${jdbc.driverClass}"></property> <property name="user" value="${jdbc.user}"></property> <property name="password" value="${jdbc.password}"></property> </bean> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="configLocation" value="classpath:mybatis-config.xml"></property> <property name="dataSource" ref="pooledDataSource"></property> <property name="mapperLocations" value="classpath:mapper/*.xml"></property> </bean> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.wang.ssm.dao"></property> </bean> <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"> <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"></constructor-arg> <constructor-arg name="executorType" value="BATCH"></constructor-arg> </bean> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="pooledDataSource"></property> </bean> <aop:config> <aop:pointcut id="txPonit" expression="execution(* com.wang.ssm.service..*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="txPonit"></aop:advisor> </aop:config> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="*"/> <tx:method name="get*" read-only="true"/> </tx:attributes> </tx:advice></beans>
3.jdbc信息 jdbc.properties
jdbc.driverClass=com.mysql.cj.jdbc.Driverjdbc.jdbcUrl=jdbc:mysql://localhost:13306/ssmcrudjdbc.user=rootjdbc.password=123456
4.MyBatis的配置文件
mybatis-config.xml
<configuration> <properties resource="jdbc.properties"/> <settings> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings> <typeAliases> <package name="com.wang.ssm.pojo"/> </typeAliases> <plugins> <plugin interceptor="com.github.pagehelper.PageInterceptor"> <property name="reasonable" value="true"/> </plugin> </plugins> <mappers> <package name="mapper"/> </mappers></configuration>
5.MyBatis逆向工程的配置
generatorConfig.xml
<generatorConfiguration> <context id="DB2Tables" targetRuntime="MyBatis3"> <commentGenerator> <property name="suppressAllComments" value="true"/> </commentGenerator> <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver" connectionURL="jdbc:mysql://localhost:13306/ssmcrud" userId="root" password="123456"> </jdbcConnection> <javaModelGenerator targetPackage="com.wang.ssm.pojo" targetProject=".\src\main\java"> <property name="enableSubPackages" value="true" /> <property name="trimStrings" value="true" /> </javaModelGenerator> <sqlMapGenerator targetPackage="mapper" targetProject=".\src\main\resources"> <property name="enableSubPackages" value="true" /> </sqlMapGenerator> <javaClientGenerator type="XMLMAPPER" targetPackage="com.wang.ssm.dao" targetProject=".\src\main\java"> <property name="enableSubPackages" value="true" /> </javaClientGenerator> <table tableName="tbl_emp" domainObjectName="Emp"/> <table tableName="tbl_dept" domainObjectName="Dept"/> </context></generatorConfiguration>
6.创建数据库表
CREATE TABLE `tbl_emp` ( `emp_id` int(11) NOT NULL AUTO_INCREMENT, `emp_name` varchar(255) DEFAULT NULL, `gender` char(1) DEFAULT NULL, `email` varchar(255) DEFAULT NULL, `d_id` int(11) DEFAULT NULL, PRIMARY KEY (`emp_id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;CREATE TABLE `tbl_dept` ( `dept_id` int(11) NOT NULL AUTO_INCREMENT, `dept_name` varchar(255) DEFAULT NULL, PRIMARY KEY (`dept_id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;
4.完成逆向工程创建
在pom.xml中依赖导入之后可见
Plugins下会出现mybatis-generator,点击下面的mybatis-generator:generate即可完成逆向工程的创建
会自动生成如下红色部分的图片
三、查询功能实现
1.需要实现的功能
(1)第一步,用 JSTL 实现(URI:/emps):
- 访问 index.html 页面。
- index.html 发送出查询员工列表的请求。
- EmController 接收请求,查出员工的数据。
- 来到index.html 页面进行展示。
- 使用 PageHelper 分页插件完成分页查询的功能。
(2)第二步,用 Ajax 实现:
- index.html 页面直接发送 Ajax 请求,进行员工分页数据的查询。
- 服务器将查出来的数据,通过 JSON 字符串的形式返回给浏览器。
- 浏览器收到 JSON 字符串后,用 JS 对JSON 解析,通过 DOM 增删改查页面。
- 返回 JSON,实现客户端的无关性。
2.利用zui前端框架快速搭建index.html页面
注意:这里是完整的前端页面
<html lang="en" 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</title> <link rel="stylesheet" th:href="@{static/dist/css/zui.min.css}"> <!-- <script th:src="@{static/dist/lib/jquery/jquery.js}"></script> <script th:src="@{static/dist/js/zui.min.js}"></script></head><body><div class="modal fade" id="empUpdateModel"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span><span class="sr-only">关闭</span></button> <h4 class="modal-title">员工修改</h4> </div> <div class="modal-body"> <form class="form-horizontal"> <div class="form-group"> <label for="empName_update_static" class="col-sm-2">empName</label> <div class="col-md-6 col-sm-10"><p type="text" name="empName" class="form-control-static" id="empName_update_static" placeholder="empName"></p><div class="help-block"></div> </div> </div> <div class="form-group"> <label for="email_update_input" class="col-sm-2">email</label> <div class="col-md-6 col-sm-10"><input type="text" name="email" class="form-control" id="email_update_input" placeholder="email@999.com"><div class="help-block"></div> </div> </div> <div class="form-group"> <label class="col-sm-2">gender</label> <div class="col-md-6 col-sm-10"><label class="radio-inline"> <input type="radio" id="gender1_update_input" name="gender" value="M" checked> 男</label><label class="radio-inline"> <input type="radio" id="gender2_update_input" name="gender" value="F"> 女</label> </div> </div> <div class="form-group"> <label for="dept_update_select" class="col-sm-2">deptName</label> <div class="col-sm-3"><select class="form-control" name="dId" id="dept_update_select"></select> </div> </div> </form> </div> <div class="modal-footer"> <button type="button" class="btn btn-default" data-dismiss="modal">关闭</button> <button type="button" class="btn btn-primary" id="emp_update_btn">更新</button> </div> </div> </div></div><div class="modal fade" id="empAddModel"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span><span class="sr-only">关闭</span></button> <h4 class="modal-title">员工添加</h4> </div> <div class="modal-body"> <form class="form-horizontal"> <div class="form-group"> <label for="empName_add_input" class="col-sm-2">empName</label> <div class="col-md-6 col-sm-10"><input type="text" name="empName" class="form-control" id="empName_add_input" placeholder="empName"><div class="help-block"></div> </div> </div> <div class="form-group"> <label for="email_add_input" class="col-sm-2">email</label> <div class="col-md-6 col-sm-10"><input type="text" name="email" class="form-control" id="email_add_input" placeholder="email@999.com"><div class="help-block"></div> </div> </div> <div class="form-group"> <label class="col-sm-2">gender</label> <div class="col-md-6 col-sm-10"><label class="radio-inline"> <input type="radio" id="gender1_add_input" name="gender" value="M" checked> 男</label><label class="radio-inline"> <input type="radio" id="gender2_add_input" name="gender" value="F"> 女</label> </div> </div> <div class="form-group"> <label for="dept_add_select" class="col-sm-2">deptName</label> <div class="col-sm-3"><select class="form-control" name="dId" id="dept_add_select"></select> </div> </div> </form> </div> <div class="modal-footer"> <button type="button" class="btn btn-default" data-dismiss="modal">关闭</button> <button type="button" class="btn btn-primary" id="emp_save_btn">保存</button> </div> </div> </div></div><div class="container"> <div class="row"> <div class="col-md-12"> <h1>SSM_CRUD</h1> </div> </div> <div class="row"> <div class="col-md-4 col-md-offset-8"> <button class="btn btn-primary" id="emp_add_model_btn">新增</button> <button class="btn btn-danger" id="emp_delete_all_btn">删除</button> </div> </div> <div class="row"> <div class="col-md-12"> <table class="table table-hover" id="emps_table"> <thead> <tr> <th><input type="checkbox" id="check_all"> </th> <th>id</th> <th>empName</th> <th>gender</th> <th>email</th> <th>deptName</th> <th>操作</th> </tr> </thead> <tbody > </tbody> </table> </div> </div> <div class="row"> <div class="col-md-6" id="page_info_area"> </div> <div class="col-md-6" id="page_nav_area"> </div> </div></div><script type="text/javascript"> /*全局变量*/ var totalRecord;//总页数 var currentPage;//当前页 /*1.页面加载完成以后,直接发送一个ajax请求,要到分页数据*/ $(function () { //去首页 to_page(1) }); function to_page(pn){ $.ajax({ url: "emps", data: "pn="+pn, type: "GET", success: function (result) { // console.log(result); // 1.解析并显示员工数据 build_emps_table(result); // 2.解析并显示分页信息 build_page_info(result); //3.解析并显示分页条信息 build_page_nav(result); } }); } // 1.解析并显示员工数据 function build_emps_table(result) { //清空表格 否则会显示上一页的数据 $("#emps_table tbody").empty(); var emps = result.extend.pageInfo.list; $.each(emps, function (index, item) { var checkBok=$(" ") var empIdTd= $(" ").append(item.empId); var empNameTd=$(" ").append(item.empName); var genderTd=$(" ").append(item.gender=='M'? "男":"女"); var emailTd=$(" ").append(item.email); var deptName=$(" ").append(item.dept.deptName); var editBtn=$("").addClass("btn btn-primary edit_btn") .append($("").addClass("icon icon-edit")).append("编辑"); //为编辑按钮添加一个自定义的属性,来表示当前员工的id editBtn.attr("edit-id",item.empId); var delBtn=$("").addClass("btn btn-danger delete_btn") .append($("").addClass("icon icon-remove-sign")).append("删除"); //为删除按钮添加一个自定义的属性,来表示删除当前员工的id delBtn.attr("del-id",item.empId); var btnTd=$(" ").append(editBtn).append(" ").append(delBtn); //append方法返回的是append $(" "). append(checkBok). append(empIdTd). append(empNameTd) .append(genderTd).append(emailTd).append(deptName) .append(btnTd) .appendTo("#emps_table tbody"); }); } // 2.解析并显示分页信息 function build_page_info(result){ $("#page_info_area").empty() $("#page_info_area").append("当前"+result.extend.pageInfo.pageNum+"页,总" + result.extend.pageInfo.pages+"页,总" +result.extend.pageInfo.total+ "条记录数"); //总页数 totalRecord=result.extend.pageInfo.pages //当前页数 currentPage=result.extend.pageInfo.pageNum } // 2.解析并显示分页条信息 function build_page_nav(result) { //page_nav_area $("#page_nav_area").empty(); var ul=$("
").addClass("pager"); var firstPageLi=$("").append($("").append("首页")); //构建翻页元素 var prePageLi=$("").append($("").append("«")); if (result.extend.pageInfo.hasPreviousPage==false){ firstPageLi.addClass("disabled"); prePageLi.addClass("disabled"); } //为元素添加点击翻页事件 firstPageLi.click(function (){ to_page(1); }); prePageLi.click(function (){ to_page(result.extend.pageInfo.pageNum-1); }); var nextPageLi=$("").append($("").append("»")); var lastPageLi=$("").append($("").append("尾页")); if (result.extend.pageInfo.hasNextPage==false){ nextPageLi.addClass("disabled"); lastPageLi.addClass("disabled"); } nextPageLi.click(function (){ to_page(result.extend.pageInfo.pageNum+1); }); lastPageLi.click(function (){ to_page(result.extend.pageInfo.pages); }); //添加首页和前一页的提示 ul.append(firstPageLi).append(prePageLi); $.each(result.extend.pageInfo.navigatepageNums,function (index,item){ var numLi=$("").append($("").append(item).attr("th:href","#")); if (result.extend.pageInfo.pageNum==item){ numLi.addClass("active") } numLi.click(function (){ to_page(item) }); ul.append(numLi); }); //添加末页和后一页的提示 ul.append(nextPageLi).append(lastPageLi); ul.appendTo(page_nav_area); } //清空表单样式及内容 function reset_from(ele){ $(ele)[0].reset(); // 清空表单样式 $(ele).find("*").removeClass("has-success has-error"); $(ele).find(".help-block").text("") } //点击新增按钮 $("#emp_add_model_btn").click(function (){ //清除表单数据 (表单重置)全部重置 reset_from("#empAddModel form") //发送ajax请求,查出不部门的信息,显示下拉列表 getDepts("#dept_add_select"); //弹出模态框 $("#empAddModel").modal({ backdrop:"static" }) }); //查出所有的部门信息并显示在下拉列表中 function getDepts(ele){ //清空之前下拉列表的值 $(ele).empty(); $.ajax({ url:"depts", type: "GET", success:function (result){ // console.log(result) // {"code":100,"msg":"处理成功","extend":{"depts":[{"deptId":1,"deptName":"测试部"},{"deptId":2,"deptName":"开发部"}]}} // 显示部门信息在下拉列表中 // $("#dept_add_select").append() $.each(result.extend.depts,function (){ var optionEle=$("").append(this.deptName).attr("value",this.deptId); optionEle.appendTo(ele) }) } }); } //检验表单数据 function validate_add_form(){ // 1.拿过来要检验数据,使用正则表达式 var empName = $("#empName_add_input").val(); var regName =/(^[a-zA_Z0-9_-]{6,16}$)|(^[\u2E80-\u9FFF]{2,5})/; // 校验名字 if (!regName.test(empName)) { // alert("用户名可以2-5位中文6-16英文和数字的组合") //应该清空这个元素之前的格式 show_validate_msg("#empName_add_input","error","用户名可以2-5位中文6-16英文和数字的组合"); return false; }else { show_validate_msg("#empName_add_input","success",""); } // 校验邮箱 var email =$("#email_add_input").val(); var regEmail=/^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/; if (!regEmail.test(email)){ // alert("邮箱格式不正确") show_validate_msg("#email_add_input","error","邮箱格式不正确"); return false; }else { show_validate_msg("#email_add_input","success",""); } return true; } //显示校验结果的提示信息 function show_validate_msg(ele,status,msg){ //应该清空这个元素之前的格式 $(ele).parent().removeClass("has-success has-error"); $(ele).next("div").text(""); if ("success"==status){ $(ele).parent().addClass("has-success"); $(ele).next("div").text(""); }else if ("error"==status){ $(ele).parent().addClass("has-error"); $(ele).next("div").text(msg); } } $("#empName_add_input").change(function (){ // 发送ajax请求校验用户名是否可用 var empName=this.value; $.ajax({ url:"checkemp", data:"empName="+empName, type:"POST", success:function (result){ if (result.code==100){ show_validate_msg("#empName_add_input","success","用户名可用"); //给按钮添加自定义属性,用于校验数据是否合法能保存 $("#emp_save_btn").attr("ajax-va","success") }else { show_validate_msg("#empName_add_input","error",result.extend.va_msg); $("#emp_save_btn").attr("ajax-va","error") } } }); }); //点击保存,保存员工 $("#emp_save_btn").click(function (){ // 1.模态框中填写的表单数据提交给服务器进行保存 // 1.1 先对要提交给服务器的数据进行校验 if (!validate_add_form()){ return false; } // 1.2判断ajax用户名校验是否成功了成功才添加 if ($(this).attr("ajax-va")=="error"){ return false; } // 2.发送ajax请求保存员工 empAddModel $.ajax({ url:"emp", type:"POST", data:$("#empAddModel form").serialize(), success:function (result){ // alert(result.msg) if (result.code==200){ //显示失败信息 //有哪个字段的错误信息就显示哪个字段的 if (undefined!=result.extend.errorFields.email){ //显示邮箱错误信息 show_validate_msg("#email_add_input","error",result.extend.errorFields.email); } if (undefined!=result.extend.errorFields.empName){ //显示员工错误信息 show_validate_msg("#empName_add_input","error",result.extend.errorFields.empName); } }else { // 员工保存成功: // 1.关闭模拟框 $("#empAddModel").modal('hide'); // 2.来到最后一页,显示刚才保存的数据 // 显示最后一页数据 to_page(totalRecord); } } }); }); //1.我们是按钮创建之前就绑定了click,所以绑不上 // 1)可以在创建按钮的时候绑定 // 2)绑定点击 .live jQuery给所有匹配的元素附加一个事件,即使是以后添加的元素再添加进来也是有效的 //jQuery新版没有live,是用on方法替代 $(document).on("click",".edit_btn",function (){ // alert("edit") // 0查出员工信息,并显示员工信息 getEmp($(this).attr("edit-id")); // 1.查出部门信息,并显示部门列表 getDepts("#dept_update_select"); // 2 弹出模态框查出 // 3.把员工的id传递给模态框的更新按钮 $("#emp_update_btn").attr("edit-id",$(this).attr("edit-id")); $("#empUpdateModel").modal({ backdrop:"static" }) }); //绑定点击删除事件 单个删除 $(document).on("click",".delete_btn",function (){ // 1.弹出是否确认删除对话 var empName= $(this).parents("tr").find("td:eq(2)").text(); var empId = $(this).attr("del-id"); if (confirm("确认删除【"+empName+"】吗?")){ // 点击确认,发送ajax请求删除即可 $.ajax({ url:"emp/"+empId, type:"DELETE", success:function (resulet){ alert(resulet.msg); to_page(currentPage); } }); } }); //获取指定id的员工 function getEmp(id){ $.ajax({ url:"emp/"+id, type:"GET", success:function (result){ // console.log(result) var empData=result.extend.emp; $("#empName_update_static").text(empData.empName); $("#email_update_input").val(empData.email); $("#empUpdateModel input[name=gender]").val([empData.gender]); $("#empUpdateModel select").val([empData.dId]) } }); }// 点击更新,更新员工信息 $("#emp_update_btn").click(function (){ // 验证邮箱是否合法 // 校验邮箱 var email =$("#email_update_input").val(); var regEmail=/^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/; if (!regEmail.test(email)){ // alert("邮箱格式不正确") show_validate_msg("#email_update_input","error","邮箱格式不正确"); return false; }else { show_validate_msg("#email_update_input","success",""); } // 2.发送ajax请求保存更新的员工数据 $.ajax({ url:"emp/"+$(this).attr("edit-id"), type:"PUT", data:$("#empUpdateModel form").serialize(), success:function (result){ // alert(result.msg); // 1.关闭对话框 $("#empUpdateModel").modal("hide"); // 2.回到本页面 to_page(currentPage); } }); });// 完成点击全选/全不选功能 $("#check_all").click(function(){ //attr获取checked是undefined //我们这些dom原生的属性;attr获取自定义属性的值 // prop修改和读取dom原生的属性 $(".check_item").prop("checked",$(this).prop("checked")); });// check_item 当手动全选的时候自动全选也要选中 $(document).on("click",".check_item",function (){ //判断当前选中的元素是不是所有元素 //checked匹配所有选中的被选中的元素 var flag=$(".check_item:checked").length==$(".check_item").length $("#check_all").prop("checked",flag) })// 点击全部删除,就批量删除 $("#emp_delete_all_btn").click(function (){ var empNames=""; var del_idstr=""; $.each($(".check_item:checked"),function (){ // this empNames+=$(this).parents("tr").find("td:eq(2)").text()+","; del_idstr+=$(this).parents("tr").find("td:eq(1)").text()+"-"; }); //取出empNames中多余的逗号 empNames=empNames.substring(0,empNames.length-1) del_idstr=del_idstr.substring(0,del_idstr.length-1) if (confirm("确认删除【"+empNames+"】吗?")){ // 发送ajax请求删除 $.ajax({ url:"emp/"+del_idstr, type:"DELETE", success:function (result){ alert(result.msg); //回到当前页面 to_page(currentPage) $("#check_all").prop("checked",false) } }); } });</script></body></html>
3.修改EmpMapper接口
由于在页面显示时,同时会显示出部门信息,所以可以在接口中添加一个多表查询的功能
…:表示前面逆向工程自动生成的方法
package com.wang.ssm.dao;import com.wang.ssm.pojo.Emp;import com.wang.ssm.pojo.EmpExample;import java.util.List;import org.apache.ibatis.annotations.Param;public interface EmpMapper { ........ // 根据条件查询带部门信息的员工表 List<Emp> selectByExampleWithDept(EmpExample example);// 根据主键查询带部门信息的员工表 Emp selectByPrimaryKeyWithDept(Integer empId); ........}
4.修改EmpMapper.xml文件
.........<resultMap id="withDeptBaseResultMap" type="com.wang.ssm.pojo.Emp"> <id column="emp_id" jdbcType="INTEGER" property="empId"/> <result column="emp_name" jdbcType="VARCHAR" property="empName"/> <result column="gender" jdbcType="CHAR" property="gender"/> <result column="email" jdbcType="VARCHAR" property="email"/> <result column="d_id" jdbcType="INTEGER" property="dId"/> <association property="dept" javaType="dept"> <id property="deptId" column="dept_id"></id> <result property="deptName" column="dept_name"></result> </association></resultMap><!--List selectByExampleWithDept(EmpExample example);--> <sql id="Withdept_Column_List"> e.emp_id, e.emp_name, e.gender, e.email, e.d_id,d.dept_id,d.dept_name </sql> <select id="selectByExampleWithDept" resultMap="withDeptBaseResultMap"> select <if test="distinct"> distinct </if> <include refid="Withdept_Column_List"/> FROM tbl_emp e LEFT JOIN tbl_dept d ON e.d_id=d.dept_id <if test="_parameter != null"> <include refid="Example_Where_Clause"/> </if> <if test="orderByClause != null"> order by ${orderByClause} </if> <if test="orderByClause == null"> ORDER BY emp_id </if> </select> <select id="selectByPrimaryKeyWithDept" resultMap="withDeptBaseResultMap"> select <include refid="Withdept_Column_List"/> FROM tbl_emp e LEFT JOIN tbl_dept d ON e.d_id=d.dept_id where emp_id = #{empId,jdbcType=INTEGER} </select>.........
5.添加 Msg 类引入链式编程
以Msg作为Controller层方法的返回值,返回状态码及前端请求的数据,方便前端处理
/ * @author 沐 * @Description:通用的返回类 * @create 2022-03-16 14:14 */public class Msg {// 状态码 private int code;// 提示信息 private String msg;// 用户要返回给浏览器的数据 private Map<String,Object> extend=new HashMap<>();// 成功返回信息 public static Msg success(){ Msg result=new Msg(); result.setCode(100); result.setMsg("处理成功"); return result; } // 失败返回信息 public static Msg fail(){ Msg result=new Msg(); result.setCode(200); result.setMsg("处理失败"); return result; } public Msg add(String key,Object value){ this.getExtend().put(key,value); return this; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public Map<String, Object> getExtend() { return extend; } public void setExtend(Map<String, Object> extend) { this.extend = extend; }}
5.编写service层的方法
@Servicepublic class EmpService { @Autowired EmpMapper empMapper; / *@author 沐 *@Description:查询所有员工 *@Date 2022/3/15 14:11 *@Param *@Return */ public List<Emp> getAll(){ return empMapper.selectByExampleWithDept(null); } }
6.编写Controller层的方法
@Controllerpublic class EmpController { @Autowired EmpService empService; / * 导入jackson包 * * @param pn * @return */ @RequestMapping("/emps") @ResponseBody public Msg getEmpWithJson( @RequestParam(value = "pn", defaultValue = "1") Integer pn ) { // 引入PageHelper分页插件//在查询之前只需要调用,传入页码,以及每页的大小 PageHelper.startPage(pn, 5);// startPage后面紧跟的这个查询就是一个分页查询 List<Emp> emps = empService.getAll();// 使用pageInfo包装查询后的结果,只需要将pageinfo交给页面就可以了// pageInfo封装了详细的信息,包括有我们查询出来的数据 navigatePages:导航分页的页码数 PageInfo pageInfo = new PageInfo(emps, 5); return Msg.success().add("pageInfo", pageInfo); }}
效果图:
四、新增功能的实现
1.需要实现的功能
-
在 index.html 页面点击“新增”弹出对话框
-
去数据库中查询部门列表,显示在对话框内
-
对用户输入的数据进行校验
-
- jQuery 前端校验格式
- Ajax 校验用户名是否重复
- 后端校验(JSR 303)
-
保存用户数据
URI 设计
/emp/{id}
,GET
请求,查询员工数据/emp
,POST
请求,保存员工数据/emp/{empId}
,PUT
请求,修改员工数据/emp/{id}
,DELETE
请求,删除员工数据
2.编写service层的方法
@Servicepublic class EmpService { ......... @Autowired EmpMapper empMapper; / *@author 沐 *@Description:查询所有员工 *@Date 2022/3/15 14:11 *@Param *@Return */ public List<Emp> getAll(){ return empMapper.selectByExampleWithDept(null); } / * 添加员工 * @param emp */ public void saveEmp(Emp emp){ empMapper.insertSelective(emp); } / * 检查是否存在empName的员工 * @param empName * @return */ public boolean checkEmp(String empName){ EmpExample example = new EmpExample(); example.createCriteria().andEmpNameEqualTo(empName); return empMapper.countByExample(example)==0; } / *@author 沐 *@Description:按照员工id查询员工 *@Date 2022/3/17 15:43 *@Param *@Return */ public Emp getEmp(Integer id){ Emp emp = empMapper.selectByPrimaryKey(id); return emp; } .........}
3.编写Controller层的方法
实现后端校验(JSR 303)
@Controllerpublic class EmpController {.......... @Autowired EmpService empService; / * 导入jackson包 * * @param pn * @return */ @RequestMapping("/emps") @ResponseBody public Msg getEmpWithJson( @RequestParam(value = "pn", defaultValue = "1") Integer pn ) { // 引入PageHelper分页插件//在查询之前只需要调用,传入页码,以及每页的大小 PageHelper.startPage(pn, 5);// startPage后面紧跟的这个查询就是一个分页查询 List<Emp> emps = empService.getAll();// 使用pageInfo包装查询后的结果,只需要将pageinfo交给页面就可以了// pageInfo封装了详细的信息,包括有我们查询出来的数据 navigatePages:导航分页的页码数 PageInfo pageInfo = new PageInfo(emps, 5); return Msg.success().add("pageInfo", pageInfo); } / * 检查用用户名是否可用 * * @param empName * @return true:代表当前姓名可用 false: 代表代表当前姓名不可用 */ @RequestMapping("/checkemp") @ResponseBody public Msg checkEmp(@RequestParam("empName") String empName) {// 1.1 先判断用户名是否是合法的表达式 String regx = "(^[a-zA_Z0-9_-]{6,16}$)|(^[\\u2E80-\\u9FFF]{2,5})";// 判断此字符串是否与给定的正则表达式匹配。以str .matches( regex )形式调用此方法会产生与表达式完全相同的结果 if (!empName.matches(regx)) { return Msg.fail().add("va_msg", "用户名必须是2-5位中文或6-16英文和数字的组合"); }// 1.2 数据库用户名校验 boolean checkEmp = empService.checkEmp(empName); if (checkEmp) { return Msg.success(); } return Msg.fail().add("va_msg", "用户名已存在"); } / * 添加并保存员工请求 * 1.支持JSR303校验 * 2. * * @return */ @RequestMapping(value = "/emp", method = RequestMethod.POST) @ResponseBody public Msg saveEmp(@Valid Emp emp, BindingResult result) { //@Valid 标识封装的数据进行正则校验 if (result.hasErrors()) {// 校验失败,在模态框中显示校验失败的错误信息 Map<String,Object> map=new HashMap<>(); List<FieldError> errors = result.getFieldErrors(); for (FieldError fieldError : errors) { System.out.println("错误的字段名为:"+fieldError.getField()); System.out.println("错误信息:"+fieldError.getDefaultMessage()); map.put(fieldError.getField(),fieldError.getDefaultMessage()); } return Msg.fail().add("errorFields",map); } empService.saveEmp(emp); return Msg.success(); } / *@author 沐 *@Description:根据id查询员工 *@Date 2022/3/17 17:14 *@Param *@Return */ @RequestMapping(value = "/emp/{id}",method = RequestMethod.GET) @ResponseBody public Msg getEmp(@PathVariable("id") Integer id){ Emp emp = empService.getEmp(id); return Msg.success().add("emp",emp); } ........}
4.index.html界面(部分)
<div class="modal fade" id="empAddModel"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span><span class="sr-only">关闭</span></button> <h4 class="modal-title">员工添加</h4> </div> <div class="modal-body"> <form class="form-horizontal"> <div class="form-group"> <label for="empName_add_input" class="col-sm-2">empName</label> <div class="col-md-6 col-sm-10"><input type="text" name="empName" class="form-control" id="empName_add_input" placeholder="empName"><div class="help-block"></div> </div> </div> <div class="form-group"> <label for="email_add_input" class="col-sm-2">email</label> <div class="col-md-6 col-sm-10"><input type="text" name="email" class="form-control" id="email_add_input" placeholder="email@999.com"><div class="help-block"></div> </div> </div> <div class="form-group"> <label class="col-sm-2">gender</label> <div class="col-md-6 col-sm-10"><label class="radio-inline"> <input type="radio" id="gender1_add_input" name="gender" value="M" checked> 男</label><label class="radio-inline"> <input type="radio" id="gender2_add_input" name="gender" value="F"> 女</label> </div> </div> <div class="form-group"> <label for="dept_add_select" class="col-sm-2">deptName</label> <div class="col-sm-3"><select class="form-control" name="dId" id="dept_add_select"></select> </div> </div> </form> </div> <div class="modal-footer"> <button type="button" class="btn btn-default" data-dismiss="modal">关闭</button> <button type="button" class="btn btn-primary" id="emp_save_btn">保存</button> </div> </div> </div></div>
效果图:
五、修改数据的实现
1.需要实现的功能
- 点击编辑按钮,弹出用户修改的模态框。
- 模态框可以回显用户的信息。
- 点击更新,完成修改操作。
2.编写Service层的方法
@Servicepublic class EmpService { ......./ * 更新员工 * @param emp */ public void updateEmp(Emp emp){ empMapper.updateByPrimaryKeySelective(emp); } ....}
3.编写Controller层的方法
@Controllerpublic class EmpController { ......./ *@author 沐 *@Description:更新并保存员工数据 *@Date 2022/3/17 17:14 *@Param *@Return * 如果直接发送ajax=put形式的请求存在问题 * * 请求体中有数据;但是employee对象封装不上 * 我们要能支持发送PUT之类的请求还要封装请求体中的数据 * 配置上FormContentFilter * 作用:能够处理PUT和DELETE请求 将请求体中的数据包装成一个map */ @ResponseBody @RequestMapping(value = "/emp/{empId}",method = RequestMethod.PUT) public Msg saveEmp(Emp emp){ empService.updateEmp(emp); return Msg.success(); } .......}
4.index.html界面(部分)
全部index.html界面见上;
<div class="modal fade" id="empUpdateModel"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span><span class="sr-only">关闭</span></button> <h4 class="modal-title">员工修改</h4> </div> <div class="modal-body"> <form class="form-horizontal"> <div class="form-group"> <label for="empName_update_static" class="col-sm-2">empName</label> <div class="col-md-6 col-sm-10"><p type="text" name="empName" class="form-control-static" id="empName_update_static" placeholder="empName"></p><div class="help-block"></div> </div> </div> <div class="form-group"> <label for="email_update_input" class="col-sm-2">email</label> <div class="col-md-6 col-sm-10"><input type="text" name="email" class="form-control" id="email_update_input" placeholder="email@999.com"><div class="help-block"></div> </div> </div> <div class="form-group"> <label class="col-sm-2">gender</label> <div class="col-md-6 col-sm-10"><label class="radio-inline"> <input type="radio" id="gender1_update_input" name="gender" value="M" checked> 男</label><label class="radio-inline"> <input type="radio" id="gender2_update_input" name="gender" value="F"> 女</label> </div> </div> <div class="form-group"> <label for="dept_update_select" class="col-sm-2">deptName</label> <div class="col-sm-3"><select class="form-control" name="dId" id="dept_update_select"></select> </div> </div> </form> </div> <div class="modal-footer"> <button type="button" class="btn btn-default" data-dismiss="modal">关闭</button> <button type="button" class="btn btn-primary" id="emp_update_btn">更新</button> </div> </div> </div></div>
效果图
六、删除数据的实现
1.需要实现的功能
-
单个删除,批量删除二合一,点击某条用户的删除按钮,弹出提示框,点击确定即可删除,也可以点击前面的复选框进行批量删除
-
- URI:
/emp/{ids}
,DELETE
请求
- URI:
2.编写Service层的方法
@Servicepublic class EmpService { ....... / *@author 沐 *@Description:根据员工id删除员工 *@Date 2022/3/17 19:01 *@Param *@Return */ public void deleteEmp(Integer id){ empMapper.deleteByPrimaryKey(id); } / *@author 沐 *@Description:批量删除 *@Date 2022/3/17 21:11 *@Param *@Return */ public void deleteBatch(List<Integer> ids){ EmpExample example = new EmpExample(); example.createCriteria().andEmpIdIn(ids); empMapper.deleteByExample(example); } ....}
3.编写Controller层的方法
@Controllerpublic class EmpController { ......./ * 单个批量二合一 * 批量:1-2-3 * 单个:1 * @param ids * @return */ @ResponseBody @RequestMapping(value="/emp/{ids}",method = RequestMethod.DELETE) public Msg deleteEmpById(@PathVariable("ids")String ids){ if (ids.contains("-")){ List<Integer> del_ids=new ArrayList<>(); String[] str_ids = ids.split("-");//组装id的数组集合 for(String str :str_ids){ del_ids.add(Integer.parseInt(str)); } empService.deleteBatch(del_ids); }else { empService.deleteEmp(Integer.parseInt(ids)); } return Msg.success(); } .......}
4.index.html界面(部分)
<script type="text/javascript"> ............//绑定点击删除事件 单个删除 $(document).on("click",".delete_btn",function (){ // 1.弹出是否确认删除对话 var empName= $(this).parents("tr").find("td:eq(2)").text(); var empId = $(this).attr("del-id"); if (confirm("确认删除【"+empName+"】吗?")){ // 点击确认,发送ajax请求删除即可 $.ajax({ url:"emp/"+empId, type:"DELETE", success:function (resulet){ alert(resulet.msg); to_page(currentPage); } }); } }); // 完成点击全选/全不选功能 $("#check_all").click(function(){ //attr获取checked是undefined //我们这些dom原生的属性;attr获取自定义属性的值 // prop修改和读取dom原生的属性 $(".check_item").prop("checked",$(this).prop("checked")); });// check_item 当手动全选的时候自动全选也要选中 $(document).on("click",".check_item",function (){ //判断当前选中的元素是不是所有元素 //checked匹配所有选中的被选中的元素 var flag=$(".check_item:checked").length==$(".check_item").length $("#check_all").prop("checked",flag) })// 点击全部删除,就批量删除 $("#emp_delete_all_btn").click(function (){ var empNames=""; var del_idstr=""; $.each($(".check_item:checked"),function (){ // this empNames+=$(this).parents("tr").find("td:eq(2)").text()+","; del_idstr+=$(this).parents("tr").find("td:eq(1)").text()+"-"; }); //取出empNames中多余的逗号 empNames=empNames.substring(0,empNames.length-1) del_idstr=del_idstr.substring(0,del_idstr.length-1) if (confirm("确认删除【"+empNames+"】吗?")){ // 发送ajax请求删除 $.ajax({ url:"emp/"+del_idstr, type:"DELETE", success:function (result){ alert(result.msg); //回到当前页面 to_page(currentPage) $("#check_all").prop("checked",false) } }); } });</script>