文章目录
由于很多图片加载不出来,点击这,看完整版
ssm_crud项目
一、项目说明
1.项目预览
2.项目的功能点
- 1.分页
- 2.数据校验
-
- jquery前端校验+JSR303后端校验
- 大量使用ajax
- Rest风格的URI:使用http协议请求方式的动词,来表示对资源的操作(GET(查询),POST(新增),PUT(修改),DELETE(删除))
3.项目技术点
- 1.基础框架 ssm(spring+springmvc+mybatis)
- 2.数据库 mysql
- 3.项目的依赖管理管理 maven
- 4.分页 pagehelper
- 5.逆向工程 Mybatis-Gennerate
二、基础环境的搭建
1.创建maven工程
- 在idea中创建maven项目,选阿帕奇的webapp
2.引入项目依赖的jar包
在pom.xml文件中导入以下jar包的依赖点击进入maven仓库中央仓库
- spring (Spring JDBC(事务控制);Spring Aspects(切面编程))
- springmvc(Spring Web MVC)
- mybatis (Mybatis;Mybatis Spring(整合包))
- 数据库连接池(c3p0),驱动包(MySQL Connect)
- 其他(Jstl Servlet Api junit)
3.引入bootstrap,一个前端的框架
- 1.去bootstrap官网下载
- 2.在webapp下新建一个static文件夹,把bootstrap的文件放进去
- bootstrap的用法,参照官方文档
- 引入bootstrap之前一点要先引入jQuery(在webapp下新建一个js文件夹,去放jQuery)
- 引入的方式
三、配置文件的编写
1.web.xml的配置
(1)启动spring容器的监听器(项目一起动,spring的容器就启动)
- 配置监听器:项目一启动,就会加载spring的配置配置文件(指定spring配置文件的位置)
<!-- 1、启动Spring的容器 -->
<!-- needed for ContextLoaderListener -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- Bootstraps the root web application context before servlet initialization -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
- spring的配置文件:主要配合和业务逻辑有关的
(2)配置前端控制器
-
用来拦截所有的请求
-
指定springmvc配置的位置
<!--2、SpringMVC的前端控制器,拦截所有请求--> <!-- The front controller of this Spring Web application, responsible for handling all application requests --> <servlet> <servlet-name>springDispatcherServlet</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> <!-- Map all requests to the DispatcherServlet for handling --> <servlet-mapping> <servlet-name>springDispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
-
注意:如图是指定springmvc位置的配置方法,也可以不指定位置。就是在与web.xml同级的目录下创建一个servlet名-servlet的文件,如图就是创建一个springDispatcherServlet-servlet.xml的文件就可以代替springmvc.xml的文件
(3)配置字符编码过滤器(防止乱码)
-
代码
<!--字符编码过滤器(所有过滤器之前) --> <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>forceRequestEncoding</param-name> <param-value>true</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>
(4)配置支持Rest风格URI的过滤器
- 因为页面无法支持delete、put请求,还需要一个过滤器
- 代码
<!--使用Rest风格的URI--> <!-- HiddenHttpMethodFilter:将普通的get/post请求转化为put/delete请求 --> <filter> <filter-name>HiddenHttpMethodFilter</filter-name> <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class> </filter> <filter-mapping> <filter-name>HiddenHttpMethodFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
2.springmvc.xml的配置
- 主要包含网站跳转逻辑的组件
(1)扫描所有的业务逻辑组件
- 在java.com.wantao包下创建bean、controller、service、dao、utils、test包
- 我们希望只扫描controller(控制器)
<!--springmvc的配置文件,包含网站跳转逻辑控制,配置 --> <context:component-scan base-package="com.wantao" use-default-filters="false"> <!--只扫描控制器--> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan>
(2)配置视图解析器
- 方便页面的解析
- 在WEB-INF下建一个view包
<!--配置视图解析器,方便页面返回--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/views/"></property> <property name="suffix" value=".jsp"></property> </bean>
(3)两个标准配置
- 代码
<!--两个标准配置--> <!-- 支持springmvc更高级的功能,比如:,JSR303校验,快捷的ajax,映射动态请求 --> <mvc:annotation-driven/> <!-- 将springmvc不能处理的请求交给tomcat --> <mvc:default-servlet-handler />
3.applicationContext.xml的配置
- 主要是配置和逻辑有关的
- Spring配置文件的核心点:(数据源、与mybatis的整合、事务控制)
(1)配置数据源(c3p0)
- 代码
<!--配置数据源--> <bean id="PoolDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="jdbcUrl" value="jdbc:mysql://47.94.229.156:3306/ssm_crud?serverTimezone = GMT"></property> <property name="driverClass" value="com.mysql.cj.jdbc.Driver"> </property> <property name="user" value="root"> </property> <property name="password" value="root"> </property> </bean>
- 也可以把连接信息写在jdbc.properties文件中
(2)和mybatis的整合配置
- 配置SqlSessionFactoryBean,他能创建SqlSessionFactory
<!--================配置spring和mabatis的整合=================== --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!--指定mybatis的全局配置文件 --> <property name="configLocation" value="classpath:mybatis-config.xml"> </property> <!--配置数据源 --> <property name="dataSource" ref="PoolDataSource"> </property> <!--配置mybatis映射文件 --> <property name="mapperLocations" value="classpath:mapper/*.xml"> </property> </bean> <!-- 设置扫描器,将mybatis的接口实现类的实现(代理对象)添加到ioc容器中 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!--扫描所有dao接口的实现,加入到IoC容器中--> <property name="basePackage" value="com.wantao.dao"> </property> </bean> <!--====================================-->
- 注意:在spring和mybatis整合的时候,并不是一定要把mapper的映射文件放到和dao层的文件一个路径,也可以在sqlSessionFactory中配置映射文件存放的位置;在MapperScannerConfiger中配置要扫描的dao层
(3)事务控制的配置
- 要能管事务,就是控制住数据源
<!-- 配置事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!--控制数据源--> <property name="dataSource" ref="PoolDataSource"> </property> </bean>
- 1.开启注解的事务,2.使用xml配置形式的事务(比较重要的都是采用配置式)
<!--开启注解事务或者使用xml配置形势的事务(主要的都是配置式)--> <aop:config> <!--切点表达式 --> <aop:pointcut expression="execution(* com.wantao.service..*(..))" id="txPointCut" /> <!--配置事物增强 --> <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut" /> </aop:config> <!--配置事物增强,事务如何切入--> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <!--代表所有方法都是事务方法--> <tx:method name="*" /> <!--以find开头的所有方法--> <tx:method name="find*" read-only="true" /> <tx:method name="get*" read-only="true" /> </tx:attributes> </tx:advice>
3.mybatis-config.xml(全局配置文件+mapper文件)
全局配置文件
- 注意:这个全局配置文件不是必须的,也可以放在放在applicationContext.xml中,以property的形势放在class为SqlSessionFactoryBeanbean中。
- 搜索mybatis的官方文档(点击进入)
- 找到表头,复制进去
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
(1)配置驼峰命名规则
- 代码
<!-- 是否开启自动驼峰命名规则(camel case)映射, 即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射。--> <settings> <setting name="mapUnderscoreToCamelCase" value="true" /> </settings>
(2)配置类型别名
- 代码
<typeAliases> <package name="com.wantao.bean" /> </typeAliases>
配置mapper文件
- 一个mapper对应一个dao,所以需要先创建数据库
- 然后用逆向工程生成需要的dao、mapper、xml
4.逆向工程
- 去网页搜索 Mybatis Generator(GitHub–>进入帮助文档)点击进入
(1)引入依赖jar
- 在pom.xml文件中添加
<!-- https://mvnrepository.com/artifact/org.mybatis.generator/mybatis-generator-core --> <dependency> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-core</artifactId> <version>1.3.7</version> </dependency>
(2)创建连接数据库的mgb.xml配置文件
- 网页位置
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<context id="DB2Tables" targetRuntime="MyBatis3">
<!-- 设置生成的文件没有注释 -->
<commentGenerator>
<property name="suppressAllComments" value="true" />
</commentGenerator>
<!-- 配置数据库连接 -->
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/ssm_crud?serverTimezone = GMT"
userId="root" password="root">
</jdbcConnection>
<javaTypeResolver>
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!--指定javaBean生成的位置-->
<javaModelGenerator targetPackage="com.wantao.bean"
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>
<!--指定dao接口生成的位置 mapper接口-->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.wantao.dao" targetProject=".\src\main\java">
<property name="enableSubPackages" value="true" />
</javaClientGenerator>
<!-- 指定每个表生成策略 -->
<table schema="DB2ADMIN" tableName="tb_emp"
domainObjectName="Employee">
</table>
<table schema="DB2ADMIN" tableName="tb_dept"
domainObjectName="Department">
</table>
</context>
</generatorConfiguration>
(3)创建MGBTest类完成逆向工程的创建
-
网页的位置
-
在test包下,创建一个MGBTest类
public class MGBTest { @Test public void gennerator() throws IOException, XMLParserException, InvalidConfigurationException, SQLException, InterruptedException { List<String> warnings = new ArrayList<String>(); boolean overwrite = true; File configFile = new File("src/main/resources/mgb.xml"); ConfigurationParser cp = new ConfigurationParser(warnings); Configuration config = cp.parseConfiguration(configFile); DefaultShellCallback callback = new DefaultShellCallback(overwrite); MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings); myBatisGenerator.generate(null); } }
-
直接执行就可以获得逆向工程的映射文件和dao层接口、mapper、xml以及pojo
(4)生成的不一定符合要求,再添加自己的需求(联合查询)
-
查员工信息的时候,希望根据员工的部门号查出员工的部门名
-
1.EmployeeMapper.java中新增两个能查出部门的方法
//带部门的查询方法List<Employee> selectByExampleWithDept(EmployeeExample example); //带部门的查询方法Employee selectByPrimaryKeyWithDept(Integer empId);
-
2.在Employee.java中引入部门信息
//希望查询员工的同时,部门信息也是查询好的。 private Department department; public Department getDepartment() { return department; } public void setDepartment(Department department) { this.department = department; }
-
3.在EmployeeMapper.xml文件中添加新建的两个带部门的方法
<sql id="Base_Column_List_With_Dept"> emp_id, emp_name, gender, email,d_id,dept_id,dept_name
5.创建测试类,测试逆向工程生成的方法
- 需求:1.测试是否数据库已经连通,生成的逆向工程是否可用;2.生成初始数据,用于后面的测试
- 分析:此时的mybatis是和spring整合的,有以下两种方法
-
在test包下创建一个MapperTest类,用于测试Dao层的方法
-
方法一:通过原始方法进行测试
//1.创建springIoC容器 ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml"); //2.从容器中获取 DepartmentMapper bean = ac.getBean(DepartmentMapper.class);
-
方法二:通过spring-test进行测试
推荐spring的项目就可以使用Spring的单元测试,看自动注入我们需要的组件 -
1.要导入spring的单元测试模块
-
@RunWith(SpringJUnit4ClassRunner.class),需要在pom.xml导入spring-test的依赖,可以在maven中央仓库搜索
<!-- https://mvnrepository.com/artifact/org.springframework/spring-test --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>4.3.7.RELEASE</version> <scope>test</scope> </dependency>
-
-
2.通过@ContextConfiguration指定spring配置文件的位置,他能自动创建出IoC容器; @RunWith(SpringJUnit4ClassRunner.class),指定具体单元测试的组件(这里使用spring的单元测试);需要注入什么组件,直接autowire注入组件
(1)测试spring
@RunWith(SpringJUnit4ClassRunner.class)//指定具体单元测试的组件(这里使用spring的单元测试) @ContextConfiguration(locations = {"classpath:applicationContext.xml"}) // (实际上也是获取spring上下文) public class DaoTest2 { @Autowired DepartmentMapper departmentMapper; public void testCRUD(){ System.out.println(departmentMapper);//测试spring配置是否ok }
结果如下:
org.apache.ibatis.binding.MapperProxy@314c8b4a
这样就说明spring的配置成功了。
(2)插入几个部门
- 用有参构造器实现插入操作,在Department中生成有参构造器
- 注意:一旦生成有参构造构造器,一定要生产无参的构造器,因为反射要经常的使用无参的构造
public class Department { private Integer deptId; private String deptName; //因为生成了有参的构造器,一定要生产无参的构造器,因为反射要经常的使用无参的构造 public Department() { } public Department(Integer deptId, String deptName) { this.deptId = deptId; this.deptName = deptName; } public Integer getDeptId() { return deptId; } public void setDeptId(Integer deptId) { this.deptId = deptId; } public String getDeptName() { return deptName; } public void setDeptName(String deptName) { this.deptName = deptName; } }
@RunWith(SpringJUnit4ClassRunner.class)//指定具体单元测试的组件 @ContextConfiguration(locations = {"classpath:applicationContext.xml"}) // (实际上也是获取spring上下文) public class DaoTest2 { @Autowired DepartmentMapper departmentMapper; @Test public void testDao() { //1.插入几个部门 departmentMapper.insertSelective(new Department(null,"测试部")); departmentMapper.insertSelective(new Department(null,"开发部")) } }
如果数据库中有数据,则说明mybatis配置正确。
这个时候进行测试的时候,不知道为啥就是连不上数据库,后来把jdbc.properties里的内容直接写入applicationContext,问题解决。
(3)测试员工插入,生成员工的数据,用于后面的开发
- 同样为了方便,给Employee生成有参无参构造器
public Employee(Integer empId, String empName, String gender, String email, Integer dId) { this.empId = empId; this.empName = empName; this.gender = gender; this.email = email; this.dId = dId; } public Employee() { }
在上面的测试类中直接加入下面的代码即可
employeeMapper.insertSelective(new Employee(null,"jerry","M","23@qq.com",1));
-
(4)重要需求:想要批量的添加员工用于测试,这个时候就需要sqlSession
-
1.首先,在applicationContext.xml中配置sqlSession
<!--配置批量的sqlSession --> <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>
-
2.在测试类中注入SqlSession
@Autowired SqlSession sqlSession;// 批量处理 3.批量生成1000个员工用于测试 @Test public void testDao() { //批量插入多个员工,使用可以批量操作的sqlSession(常用语生成测试数据) EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class); for (int i = 0; i < 1000; i++) { String uuid = UUID.randomUUID().toString().substring(0, 5) + i; employeeMapper.insertSelective(new Employee(null, uuid, "M", uuid + "@qq.com", 1)); }
查看数据库的数据,如果加入,则成功
到此,测试就完成了
四、完成查询逻辑
(一)不分页查询
1.访问index.jsp页面
2.index.jsp页面发出查询员工列表请求
-
规定查询请求的URI为
/emps
-
在index.jsp页面直接转到当前项目下的emps请求
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" isELIgnored="false" %> <jsp:forward page="/emps"></jsp:forward>
3.EmployeeController来接收请求,查出员工数据
-
想要接受到emps的请求,就要写一个处理器,在controller包中,写一个EmployeeController的类,用这个类处理员工的增删改查请求。
-
在EmployeeController的类中添加查找所有员工的方法
@Controller public class EmployeeController{ @Autowird EmployeeService employeeService; @RequestMapping("/emps") public String getEmps(){ List<Employee> emps = employeeService.getAll(); return "show"; } }
-
查询需要service层的方法,在controller层自动注入EmployeeService类,让service层查询方法,所以,要在service包下创建EmployeeService.java
-
在EmployeeService类中,创建需要的方法
@Service public class EmployeeService { @Autowired EmployeeMapper employeeMapper; public List<Employee> findAllEmployee() { //查询所有的(带上部门信息),所以调节为null return employeeMapper.selectByExampleWithDept(null); }
-
查询还需要Mapper里相应的方法,自动注入(@Autowired)
通过视图解析器,拼串后会来到/WEB-INF/views/show.jsp页面做展示
来到show.jsp页面展示
- 在WEB-INF下创建views包,在包下创建show.jsp
(二)分页查询
1. 在controller层中传入要查询的页数,默认值为1
- 在controller层中传入要查询的页数,默认值为1
@GetMapping(value = "/emps") @ResponseBody public String showEmployees(@RequestParam(defaultValue = "1", value = "pn") Integer pn,Model model) { //引入PageHelper分页插件 PageHelper.startPage(pn, 5);//第一个参数是:从第几页开始查,第二个参数为:每页5个数据 List<Employee> employees = employeeService.findAllEmployee(); //用pageInfo封装然后交给页面,传入查询出来的数据 PageInfo pageInfo=new PageInfo(employees,5);//连续显示5页 model.addAttribute("pageInfo",pageInfo); return "show"; }
2. 引入PageHelper分页插件
- 搜索pagehelper插件(GitHub)–》中文文档点击进入
//获取第1页,10条内容,默认查询总数count PageHelper.startPage(1, 10); List<Country> list = countryMapper.selectAll(); //用PageInfo对结果进行包装 PageInfo page = new PageInfo(list); //测试PageInfo全部属性 //PageInfo包含了非常全面的分页属性 assertEquals(1, page.getPageNum());//当前页码 assertEquals(10, page.getPageSize());//每页有多少条记录 assertEquals(1, page.getStartRow());//开始的记录 assertEquals(10, page.getEndRow());//结束的记录 assertEquals(183, page.getTotal());//总记录数 assertEquals(19, page.getPages());//总页码数 assertEquals(1, page.getFirstPage());//第一页 assertEquals(8, page.getLastPage());//最后一页 assertEquals(true, page.isFirstPage());//是否第一页 assertEquals(false, page.isLastPage());//是否最后一页 assertEquals(false, page.isHasPreviousPage());//是否有前一页 assertEquals(true, page.isHasNextPage());//是否有后一页
- 用PageInfo包装查出来的数据,查询出来的数据写到Model中
- 在pom.xml文件中引入相应的依赖
<dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.0.0</version> </dependency>
- 在mybatis全局配置中注册这个插件:–》中文文档往下翻
<!-- 引入分页插件--> <plugins> <plugin interceptor="com.github.pagehelper.PageInterceptor"> <!-- 分页参数合理化,不可能<1,或者>最大页码--> <property name="reasonable" value="true"/> </plugin> </plugins>
3.写单元测试(用spring的单元测试),测试利用分页插件能否取到值
- 1.在test包下,创建一个MVCTest的测试类
- 2.spring4测试的时候,需要servlet3.0的支持,导包的时候,需导入3.0以上的
@RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration//获取web的IoC @ContextConfiguration(locations= {"classpath:applicationContext.xml","classpath:springmvc.xml"}) public class EmployeeControllerTest { //传入Springmvc的IoC @Autowired WebApplicationContext context;//传入Springmvc的ioc,为了能使它自动注入,需要@WebAppConfiguration MockMvc mockMvc;//虚拟mvc请求,获取到处理结果 @Before//要想使用MockMvc,需要初始化 public void init() {//需要被创建,才可以使用 mockMvc=MockMvcBuilders.webAppContextSetup(context).build(); } @Test public void testPage() throws Exception { //模拟请求,拿到返回值 MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get("/emps").param("pn","5")).andReturn(); //请求成功后请求域中有pageInfo对象,我们取出pageInfo进行验证 MockHttpServletRequest request=result.getRequest(); PageInfo pageInfo=(PageInfo) request.getAttribute("pageInfo"); System.out.println("当前页码"+pageInfo.getPageNum()); System.out.println("总页码数"+pageInfo.getPages()); System.out.println("总记录数"+pageInfo.getTotal()); System.out.println("当前页面需要连续显示的页码"); int [] nums = pageInfo.getNavigatepageNums(); for(int i : nums) { System.out.print(" "+i); } //获取员工数据 List<Employee> employees = pageInfo.getList(); for(Employee employee:employees{ System.out.println("ID"+employee.getEmpId()+"===>Name:"+employee.getEmpName()); } } }
五、完成查询界面构建(bootstrap)
- 逻辑:先来到index.sjp首页,首页会直接转到的emps请求,controller收到请求后,查询到所有的数据和分页信息后,然后来到show.jsp页面,所以我们在show.jsp页面展示员工列表就行了。
1.引入bootstrap
- 开头代码
<%//存入当前项目的名称找 pageContext.setAttribute("APP_PATH",request.getContextPath()); %> <link rel="stylesheet" href="${APP_PATH}/static/bootstrap-3.3.7-dist/css/bootstrap.min.css"></link> <script type="text/javascript" src="${APP_PATH}/static/bootstrap-3.3.7-dist/js/bootstrap.min.js"></script> <script type="text/javascript" src="${APP_PATH}/static/jquery-3.1.1.min.js"></script>
2.利用bootstrap搭建页面
创建栅格点击进入
- 根据官方文档可以得知:把div的class设为container即可设为栅格系统
- 分成四行,第一行显示标题,第二行显示新增和删除的按钮,第三行显示查询出的数据,第四行显示分页信息
- 每一行的class叫row,每一列的为col-md-(数字1-12)
<div class="container"> <!--标题 --> <div class="row"> <div class="col-md-12"> <h1>SSM-CRUD</h1> </div> </div> <!--按钮 --> <div class="row"></div> <!--显示表格数据 --> <div class="row"></div> <!--显示分页信息--> <div class="row"></div> </div>
3.添加删除和新增按钮
- 设置按钮的位置(在列偏移中设置自己占4列,偏移8列)
<!--按钮 --> <div class="row"> <div class="col-md-4 col-md-offset-8"> <button>新增</button> <button>删除</button> </div> </div>
- 美化按钮(根据官方文档,只需要给相应按钮的class增加值即可)
<!--按钮 --> <div class="row"> <div class="col-md-4 col-md-offset-8"> <button class="btn btn-primary">新增</button> <button class="btn btn-danger">删除</button> </div> </div>
4.添加显示数据的table
- 设置table的样式
<!--显示表格数据 --> <div class="row"> <div class="col-md-12"> <table class="table table-hover"> <tr> <th>#</th> <th>empName</th> <th>gender</th> <th>email</th> <th>deptName</th> <th>操作</th> </tr> <table> </div> </div>
5.添加分页显示
- 组件–》分页,在里面添加分页信息
现在页面已经搭起来了,后面要做的就是把查出来的数据,放到页面中显示。<!--显示分页信息--> <div class="row"> <!--分页文字信息 --> <div class="col-md-6"> </div> <!--分页条信息--> <div class="col-md-6"> <nav aria-label="Page navigation"> <ul class="pagination"> <li><a href="#">首页</a></li> <li> <a href="#" aria-label="Previous"> <span aria-hidden="true">«</span> </a> </li> <li><a href="#">1</a></li> <li><a href="#">2</a></li> <li><a href="#">3</a></li> <li><a href="#">4</a></li> <li><a href="#">5</a></li> <li> <a href="#" aria-label="Next"> <span aria-hidden="true">»</span> </a> </li> <li><a href="#">末页</a></li> </ul> </nav> </div> </div>
六、把数据插到页面中
1.引入标签库
- 员工的数据需要遍历得到,引入相应的标签库
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
2.遍历员工数据并填充到相应的位置
- 遍历的取出查出的数据,之前已经把数据都放到了pageInfo里面,在list里面存着
<c:forEach items="${pageInfo.list}" var="emp">
- 把取出的数据填充到相应的位置,并为 每一行的数据添加编辑和删除按钮
这样运行就可以看到:数据已经填充好了<c:forEach items="${pageInfo.list}" var="emp"> <tr> <th>${emp.empId }</th> <th>${emp.empName }</th> <th>${emp.gender=="M"?"男":"女" }</th> <th>${emp.email}</th> <th>${emp.department.deptName }</th> <th> <th> <button class=" btn btn-info"> <span class="glyphicon glyphicon-pencil" aria-hidden="true"></span>编辑 </button> <button class="btn btn-primary"> <span class="glyphicon glyphicon-remove" aria-hidden="true"></span>删除 </button> </th> </tr> </c:forEach>
3.填充分页信息
(1) 填充文字信息
- 写在前6列
<!-- 分页文字信息 --> <div class="col-md-6"> <p>当前第${pageInfo.pageNum}页,共${pageInfo.pages}页,共${pageInfo.total }条记录</p> </div>
(2) 填充分页条信息
- 分页条逻辑
-
- 1.如果你是当前页码,让它高亮显示
-
- 2.如果当前页码是第一页,首页和上一页为关闭状态
-
- 3.如果当前页码是最后一页,末页和下一页为关闭状态
-
- 4.点击第几页就跳到第几页(包括首页、末页、上一页、下一页)
<!-- 分页条信息 --> <div class="col-md-6"> <nav aria-label="page navigation"> <ul class="pagination"> <li><a href="${APP_PATH}/emps?pn=1">首页</a></li> <!-- 如果是首页则禁止点击前一页 --> <c:if test="${pageInfo.isFirstPage }"> <li class="disabled"><a href="#" aria-label="Previous"><spanaria-hidden="true">«</span></a></li> </c:if> <c:if test="${!pageInfo.isFirstPage }"> <li><a href="${APP_PATH}/empspn=${pageInfo.prePage}" aria-label="Previous"><span aria-hidden="true">«</span></a></li> </c:if> <!-- 显示连续的页面并使得当前页面为高亮 --> <c:forEach items="${pageInfo.navigatepageNums }" var="pageNum"> <%--判断是不是当前查的页面,如果是高亮,不是就不高亮--%> <c:if test="${pageNum==pageInfo.pageNum }"> <li class="active"><a href="#">${pageNum }</a></li> </c:if> <%--判断不是当前查的页面,如点击哪页跳到哪一页--%> <c:if test="${pageNum!=pageInfo.pageNum }"> <li><a href="${APP_PATH}/emps?pn=${pageNum}">${pageNum }</a></li> </c:if> </c:forEach> <c:if test="${pageInfo.isLastPage }"> <li class="disabled"><a href="#" aria-label="Next"> <span aria-hidden="true">»</span></a></li> </c:if> <c:if test="${!pageInfo.isLastPage }"> <li><a href="${APP_PATH}/emps?pn=${pageInfo.nextPage}" aria-label="Next"> <span aria-hidden="true">»</span></a></li> </c:if> <li><a href="${APP_PATH}/emps?pn=${pageInfo.pages}">末页</a></li> </ul> </nav>
需求分析
- 遇到的问题:此前,完成了在首页直接转到/emps请求,由控制器收到,查出分页的数据以及分页的信息,然后转发到list页面。使用<c:foreEach> 和EL表达式把页面遍历展示出来。但是,现在的做法,只能适用于浏览器/服务器的交互模型。而在移动互联网的时代,发请求的客户端不仅仅是浏览器,有可能是安卓客户端,还有可能是iOS客户端,如果是他们给服务器发请求的话,响应给他们的是一系列页面,他们很难解析。
- 解决方法:服务器把数据,以json的形式,返回给一系列的客户端
- 为了满足浏览器、安卓、iOS的要求,用ajax请求
0.业务逻辑
- 1.index.jsp页面,直接发送ajax请求进行员工的分页查询
- 2.服务器将查出的数据,以json字符串的形式返回给浏览器
- 3.浏览器收到js字符串。可以使用js对json进行解析,使用js通过dom增删改的方式改变页面。
- 4.返回json。实现客户端的无关性。
1.在Empcontroller类中写一个返回json字符串的方法
- 页面需要分页数据,可以直接返回PageInfo对象,用@ResponseBody就能直接把对象转为json字符串
- 要想@ResposeBody正常工作需要导入jackson包
-
- 去中央仓库搜索Jackson databind
-
- 在pom.xml中引入相应的依赖
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.10.0</version> </dependency>
- 在pom.xml中引入相应的依赖
- controller中的方法为
@GetMapping(value = "/emps") @ResponseBody public PageInfo showEmployees(@RequestParam(defaultValue = "1", value = "pn") Integer pn,Model model) { //引入PageHelper分页插件 PageHelper.startPage(pn, 5);//第一个参数是:从第几页开始查,第二个参数为:每页5个数据 List<Employee> employees = employeeService.findAllEmployee(); //用pageInfo封装然后交给页面,传入查询出来的数据 PageInfo pageInfo=new PageInfo(employees,5);//连续显示5页 return pageInfo; }
2.创建一个专门返回json数据的类
- 处理上面的数据后,成功失败这些状态信息,应该通知给浏览器。所以,创建一个通用的能带有状态信息的返回json数据的类
- 在bean包下创建一个Message类,完成上面的功能
public class Message { // 状态码(自定义)200 成功 404 失败 private int code; private String msg; // 服务器要返回给浏览器的数据 private Map<String, Object> data = new HashMap<String, Object>(); //请求成功 public static Message success() { Message message = new Message(); message.setCode(200); message.setMsg("处理成功"); return message; } //请求失败 public static Message fail() { Message message = new Message(); message.setCode(404); message.setMsg("处理失败"); return message; } //定义快捷添加信息的方法(方面链式操作) public Message add(String key, Object value) { //以下两种形式任选其一 this.data.put(key, value); //this.getData().put(key, value); return this; } public Message() { super(); // TODO Auto-generated constructor stub } 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> getData() { return data; } public void setData(Map<String, Object> data) { this.data = data; } }
- 然后希望返回给客户端的数据带上状态信息,于是要修改EmployeeController类里的返回值
@GetMapping(value = "/emps") @ResponseBody public Message showEmployeesWithJson(@RequestParam(defaultValue = "1", name = "pn") Integer pn, Model model) { PageHelper.startPage(pn, 10);// 后面紧跟的查询为分页查询,参数值为一页有多少数据。 List<Employee> employees = employeeService.findAllEmployee(); PageInfo pageInfo = new PageInfo(employees, 5);// 用pageInfo封装然后交给页面 return Message.success().add("pageInfo", pageInfo); }
- 此时在浏览器输入/emps请求,即可得到json的字符串如下
{"code":200,"msg":"处理成功","data":{"pageInfo":{"total":1150,"list":[{"empId":1,"empName":"selenium","gender":"M","email":"895484122@qq.com","dId":1,"department":{"deptId":1,"deptName":"行政部"}},{"empId":2,"empName":"9a1820","gender":"M","email":"9a1820@qq.com","dId":1,"department":{"deptId":1,"deptName":"行政部"}},{"empId":3,"empName":"f893d1","gender":"M","email":"f893d1@qq.com","dId":1,"department":{"deptId":1,"deptName":"行政部"}},{"empId":4,"empName":"409742","gender":"M","email":"409742@qq.com","dId":1,"department":{"deptId":1,"deptName":"行政部"}},{"empId":5,"empName":"dd0e63","gender":"M","email":"dd0e63@qq.com","dId":1,"department":{"deptId":1,"deptName":"行政部"}},{"empId":6,"empName":"270e54","gender":"M","email":"270e54@qq.com","dId":1,"department":{"deptId":1,"deptName":"行政部"}},{"empId":7,"empName":"1dc865","gender":"M","email":"1dc865@qq.com","dId":1,"department":{"deptId":1,"deptName":"行政部"}},{"empId":8,"empName":"c7a8e6","gender":"M","email":"c7a8e6@qq.com","dId":1,"department":{"deptId":1,"deptName":"行政部"}},{"empId":9,"empName":"f78987","gender":"M","email":"f78987@qq.com","dId":1,"department":{"deptId":1,"deptName":"行政部"}},{"empId":10,"empName":"1c8b78","gender":"M","email":"1c8b78@qq.com","dId":1,"department":{"deptId":1,"deptName":"行政部"}}],"pageNum":1,"pageSize":10,"size":10,"startRow":1,"endRow":10,"pages":115,"prePage":0,"nextPage":2,"isFirstPage":true,"isLastPage":false,"hasPreviousPage":false,"hasNextPage":true,"navigatePages":5,"navigatepageNums":[1,2,3,4,5],"navigateFirstPage":1,"navigateLastPage":5,"firstPage":1,"lastPage":5}}}
- 以上就是我们需要解析的数据。接下来,我们就从首页出发,发一个ajax请求,拿到这些数据,对这些数据拿json解析出来,使用dom增删改的形式把数据显示出来。
1.index.jsp页面,直接发送ajax请求进行员工的分页查询
(1)准备工作
- 原来是要发请求跳转到别的页面,现在直接在index页面中显示
- 把原来的的index.jsp重命为index1.jsp,然后重新建一个index.jsp页面
- 把show.jsp页面的代码全选复制到index.jsp内
- 把分页的文字信息删掉,分页条删掉,表格数据的显示<c:foreach>也删掉 ,以后的分页文字信息、分页条信息、表格的数据显示全是对于json数据的解析。
(2)从index.jsp发起请求
- 页面加载完成以后,直接发送一个ajax请求,要到一个分页数据
<script type="text/javascript"> $(function () { $.ajax({ url: "${APP_PATH}/emps", data: "pn=1", type: "get", success: function (result) {//请求成功的回调函数,result就是服务器响应给浏览器的数据 console.log(result);//到控制台,查看是否有数据 } }); }); </script>
(3)显示查询信息
- 查询界面的显示
- table的细化
<!--显示表格数据 --> <div class="row"> <div class="col-md-12"> <table class="table table-hover"> <thead> <tr> <th>#</th> <th>empName</th> <th>gender</th> <th>email</th> <th>deptName</th> <th>操作</th> </tr> </thead> <tbody> </tbody> <table> </div> </div>
(4)解析list里的员工数据
- 把员工的数据和操作的按钮一起添加
function build_emps_table(result) { //因为发送ajax请求,页面无刷新,需要先清空table表格,避免重复出现 $("#emps_table tbody").empty(); //得到所有员工的数据 var emps = result.data.pageInfo.list; //遍历员工数据,回调函数:第一个参数是索引,第二个是对象 $.each(emps, function (index, item) { //alert(item.empName);//测试一下能不能拿到员工数据 var checkBoxTd = $("<td><input type='checkbox' class='check_item'/></td>"); var empIdTd = $("<td></td>").append(item.empId); var empNameTd = $("<td></td>").append(item.empName); var genderTd = $("<td></td>").append(item.gender == "M" ? "男" : "女"); var emailTd = $("<td></td>").append(item.email); var deptNameTd = $("<td></td>").append(item.department.deptName); /* <button class=" btn btn-info"> <span class="glyphicon glyphicon-pencil" aria-hidden="true">编辑</span> </button>*/ var editBtn = $("<button></button>").addClass("btn btn-info edit_btn") .append("<span></span>").addClass("glyphicon glyphicon-pencil") .append("<span></span>").addClass("btn btn-default btn-sm") .append("编辑"); //为编辑按钮添加一个自定义的属性,来表示当前员工的id editBtn.attr("edit-id", item.empId); var delBtn = $("<button></button>").addClass("btn btn-primary delete_btn") .append("<span></span>").addClass("glyphicon glyphicon-remove") .append("<span></span>").addClass("btn btn-default btn-sm") .append("删除"); //为删除按钮添加一个自定义的属性,来表示当前删除的员工id delBtn.attr("del-id", item.empId); //把编辑和删除放到同一个单元格中 var btnTd = $("<td></td>").append(editBtn).append(" ").append(delBtn); //可以链式操作的原因:append方法执行完成以后还是返回原来的元素 $("<tr></tr>").append(checkBoxTd) .append(empIdTd) .append(empNameTd) .append(genderTd) .append(emailTd) .append(deptNameTd) .append(btnTd) .appendTo("#emps_table tbody");//把它添加到的位置 }); }
(5)解析分页信息
- 显示分页信息和分页条信息
//显示分页信息 function build_page_info(result) { //在append之前清空分页信息 $("#page_info_area").empty(); $("#page_info_area").append("当前第" + result.data.pageInfo.pageNum + "页," + "共" + result.data.pageInfo.pages + "页," + "共" + result.data.pageInfo.total + "条记录"); totalRecord = result.data.pageInfo.total; currentPage = result.data.pageInfo.pageNum; } //解析分页条 function build_emps_nav(result) { //清空分页条 $("#page_nav_area").empty(); var ul = $("<ul></ul>").addClass("pagination"); /*构建元素*/ var firstPageLi = $("<li></li>").append($("<a></a>").append("首页").attr("href", "#")); var prePageLi = $("<li></li>").append($("<a></a>").append("«")); //判断有没有前一页,如果没有前一页,设置上一页和首页不能点 if (result.data.pageInfo.hasPreviousPage == false) { firstPageLi.addClass("disabled"); prePageLi.addClass("disabled"); } else {//在元素没有被禁用的时候才添加绑定事件 //为元素添加点击翻页的事件 firstPageLi.click(function () { to_page(1); }); prePageLi.click(function () { to_page(result.data.pageInfo.pageNum - 1); }); } var nextPageLi = $("<li></li>").append($("<a></a>").append("»")); var lastPageLi = $("<li></li>").append($("<a></a>").append("末页").attr("href", "#")); //如果没有下一页,设置下一页和末页不能点 if (result.data.pageInfo.hasNextPage == false) { nextPageLi.addClass("disabled"); lastPageLi.addClass("disabled"); } else {//在没有被禁用的时候才能发请求 nextPageLi.click(function () { to_page(result.data.pageInfo.pageNum + 1); }); lastPageLi.click(function () { to_page(result.data.pageInfo.pages); }); } //添加首页和前一页的提示 ul.append(firstPageLi).append(prePageLi); //遍历查询出的数据,并添加到ul $.each(result.data.pageInfo.navigatepageNums, function (index, item) { var numLi = $("<li></li>").append($("<a></a>").append(item));//取出来的当前元素 if (result.data.pageInfo.pageNum == item) { //定位当前的也为亮色 numLi.addClass("active"); } //给numLi绑定单击事件,一点击,发ajax请求,去当前页面 numLi.click(function () { to_page(item); }); ul.append(numLi); }); //给ul添加下一页和末页 ul.append(nextPageLi).append(lastPageLi); //把ul添加到nav var navEle = $("<nav></nav>").append(ul); //最后把导航条添加到要显示的nav中 navEle.appendTo("#page_nav_area"); }
- 需要把从index页面发送请求的方式改一下,改成跳转页面的方法
function to_page(pn) { $.ajax({ url:"${APP_PATH}/emp", data:"pn="+pn, type:"get", success:function(result){//请求成功的回调函数,result就是服务器响应给浏览器的数据 //console.log(result); //1.解析并显示员工数据 bulid_emps_table(result); //2.解析并显示分页信息 //分页文字信息 build_page_info(result); //分页条 build_emps_nav(result); } }); };
这样用户的信息就显示完全了,下面用ajax来完成剩下的功能
剩余项目
- 剩余的项目都用ajax请求完成
- 用rest风格的URI
REST风格
URI
路径 | 请求类型 | 需求 |
---|---|---|
/em/{id} | GET | 查询员工 |
/emp | POST | 保存员工 |
/em/{id} | PUT | 修改员工 |
/em/{id} | DELETE | 删除员工 |
八、用户新增
0.逻辑
- 1.在index.jsp页面点击“新增”
- 2.弹出新增对话框(模态框)
- 3.去数据库查询部门列表显示在对话框中
- 4.用户输入数据,并进行校验,完成保存
1.点击新增,弹出模态框
(1)在页面中引入模态框
-
去bootstrap官网拷贝模态框
<!-- 用户新增模态框 --> <div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel"> <div class="modal-dialog" role="document"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> <h4 class="modal-title" id="myModalLabel">Modal title</h4> </div> <div class="modal-body"> ... </div> <div class="modal-footer"> <button type="button" class="btn btn-default" data-dismiss="modal">Close</button> <button type="button" class="btn btn-primary">Save changes</button> </div> </div> </div> </div>
-
为新增按钮添加id
<button class=" btn btn-danger"> <span class="glyphicon glyphicon-plus" aria-hidden="true" id="emp_add_model_btn">新增</span> </button>
(2)点击新增,弹出模态框
-
为新增按钮绑事件:结合文档中模态框的用法
//添加按钮事件,点击按钮弹出模态框 $("#emp_add_model_btn").click(function () { //清除表单数据(表单重置(表单的数据,表单的样式))dom reset_form("#empAddModal form"); //$("#empAddModal form")[0].reset(); //发送ajax,查出部门信息,显示在下拉列表中 getDepts("#dept_add_select"); $("#empAddModal").modal({//点击背景不关闭模态框 backdrop: "static" }); });
(3)把模态框需要的样子定义出来
-
对标题、按钮修改,添加表单(水平表单,在bootstrap中找)
<!-- 员工添加的模态框 --> <div class="modal fade" id="empAddModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel"> <div class="modal-dialog" role="document"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span> </button> <h4 class="modal-title" id="myModalLabel">员工新增</h4> </div> <div class="modal-body"> <form class="form-horizontal"> <div class="form-group"> <label for="empName_add_input" class="col-sm-2 control-label">empName</label> <div class="col-sm-10"> <input type="text" name="empName" class="form-control" id="empName_add_input" placeholder="empName"> <span class="help-block"></span> </div> </div> <div class="form-group"> <label for="email_add_input" class="col-sm-2 control-label">email</label> <div class="col-sm-10"> <input type="text" name="email" class="form-control" id="email_add_input" placeholder="emil@qq.com"> <span class="help-block"></span> </div> </div> <div class="form-group"> <label class="col-sm-2 control-label">gender</label> <div class="col-sm-10"> <label class="radio-inline"> <input type="radio" name="gender" id="gender1_add_input" value="M" checked="checked"> 男 </label> <label class="radio-inline"> <input type="radio" name="gender" id="gender2_add_input" value="F"> 女 </label> </div> </div> <div class="form-group"> <label class="col-sm-2 control-label">deptName</label> <div class="col-sm-3"> <%--部门提交部门id--%> <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>
2.填充模态框中的部门信息
- 需求:希望部门信息是从数据库中查到的
- 分析:弹出模态框之前,发ajax请求,查出所有的部门信息,把部门信息显示到下拉列表中。
- 在index.jsp页面写一个查询部门信息的ajax请求的方法
//查出所有的部门信息,并显示在下拉列表中 function getDepts(ele) { //清空之前下拉列表的值 $(ele).empty(); $.ajax({ url: "${APP_PATH}/depts", type: "GET", success: function (result) { //console.log(result);//打印在控制台,用于测试 //显示部门信息在模态框中 //这个是按id找的,也可以按标签找$("#empAddModal select") //$("#dept_add_select").append("") //此时需要遍历的路径是result.data.depts,函数需要的变量可以写在函数的()里,如果不写,用this也可以 $.each(result.data.depts, function () { var optinEle = $("<option></option>").append(this.deptName).attr("value", this.deptId); optinEle.appendTo(ele); }); } })
- 控制器中需要有一个拦截他的方法,在controller包下创建一个DepartmentController.java,用来处理和部门有关的请求
@Controller public class DepartmentController { @Autowired private DepartmentService departmentService; /** * * @return返回所有的部门信息(json数据) */ @GetMapping(value = "/depts") @ResponseBody public Message getAllDepts() { //查出的所有部门信息 List<Department> list = departmentService.getAllDepts(); return Message.success().add("depts",list); } }
- 在service包下创建DepartmentService
@Service("departmentService") public class DepartmentService { @Autowired private DepartmentMapper departmentMapper; public List<Department> getAllDepts() { List<Department> list=departmentMapper.selectByExample(null); return list; } }
- 在打开模态框之前,发送查询部门的方法
3.点击保存按钮保存数据
0.逻辑
- 点击保存,发送ajax请求,保存数据
1.方法
-
为保存按钮添加id
id="emp_save_btn"
-
为保存按钮绑事件
//点击保存员工 $("#emp_save_btn").click(function () { //1.发送ajax请求保存员工 //jquery中提供了简单的把表单数据拿出来的方法(序列表格中的内容为字符串):serialize(),用于ajax请求,下面是个测试 /*alert($("#empAddModal form").serialize());*/ $.ajax({ url: "${APP_PATH}/emp", type: "post", data: $("#empAddModal form").serialize(), success: function (result) { //测试是否保存成功 //alert(result.msg); //员工保存成功后:1.关闭模态框; $("#empAddModal").modal('hide'); // 2.来到最后一页,显示刚才插入的数据 //发送ajax请求,显示最后一页(只要传一个足够大的数,分页插件都会默认到最后一页) //这里我吗用记录数,因为记录数,一定是大于页数的 to_page(totalRecord); } }); });
-
在EmployeeContorller中添加保存的方法
@PostMapping(value = "/emp") @ResponseBody public Message saveEmployee( Employee employee) { employeeService.saveEmployee(employee); return Message.success(); }
-
在EmployeeService中创建员工保存的方法
/** * @param * @return void * @description 新增用户 */ public void saveEmployee(Employee employee) { employeeMapper.insertSelective(employee); }
- 遇到的问题:如果用户名相同的时候,也是可以添加上,这样不符合业务的需求;
- 解决的方法:保存数据之前校验
4.保存数据之前先校验(前端校验)
(1)如果校验失败,返回false
(2)校验的方法
- 拿到需要校验的元素
- 通过正则表达式校验(jQuery文档)
function validate_add_form() { //1.拿到要校验的数据,使用正则表达式 var empName = $("#empName_add_input").val();//通过id拿到值 var regName = /(^[a-zA-Z0-9_-]{6,16}$)|(^[\u2E80-\u9FFF]{2,5})/ /*alert(regName.test(empName));//先测试一下成功吗 return false;//return false先让数据不提交*/ //先校验用户名信息是否合法 if(!regName.test(empName)){ //1.如果校验失败 // alert("用户名可以是2-5位中文或者是2-16位英文和数字的组合"); return false; } //2.如果没有失败,校验邮箱信息 var email = $("#email_add_input").val(); var regEmail = /^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/; if(!regEmail.test(email)){ //如果email校验失败了 alert("邮箱格式不正确"); return false; } //都成功,返回true return ture; }
这样就完成了校验,但是,这种(alert)弹出的方式不太美观
(3)使用bootstrap中的表单校验状态完善校验显示的功能
- 把弹窗提示删掉
- 给用户名和邮箱的input框里添加span标签
<span class="help-block"></span>
- 给父元素添加错误的样式
//校验表单数据 function validate_add_form() { //1.拿到要校验的数据,使用正则表达式 var empName = $("#empName_add_input").val();//通过id拿到值 var regName = /(^[a-zA-Z0-9_-]{6,16}$)|(^[\u2E80-\u9FFF]{2,5})/ if(!regName.test(empName)){ //1.如果校验失败 // alert("用户名可以是2-5位中文或者是2-16位英文和数字的组合");//美化替换他 用show_validate_msg(ele,status,msg)方法替换他 $("#empName_add_input").parent().addClass("has-error"); $("#empName_add_input").next("span").text("用户名可以是2-5位中文或者是2-16位英文和数字的组合"); return false; }else{ $("#empName_add_input").parent().addClass("has-success"); $("#empName_add_input").next("span").text(""); } //2.如果没有失败,校验邮箱信息 var email = $("#email_add_input").val(); var regEmail = /^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/; if(!regEmail.test(email)){ //如果email校验失败了 // alert("邮箱格式不正确");//美化替换他 $("#email_add_input").parent().addClass("has-error"); $("#email_add_input").next("span").text("邮箱格式不正确"); return false; }else { $("#email_add_input").parent().addClass("has-success"); $("#enum_add_input").next("span").text(""); } //一切都正常,返回true return true; }
这个时候:如果第一次输入的是错误的,会提示错误信息,输入框会变成红色;如果第二次输入正确,提示的文字框会消失,但是,输入框依旧是红色的;原因就是元素的class没有刷新,把has-error和has-success放在一起了
- 清空样式
(4)把校验的方法抽取出来
- 抽取公共的方法
function show_validate_msg(ele, status, msg) { //如果第一次输入失败,即使第二次成功也是红框,所以:应该清空当前校验状态 $(ele).parent().removeClass("has-sucess has-error"); $(ele).next("span").text("");//文本内容默认没有 if ("success" == status) { $(ele).parent().addClass("has-success"); $(ele).next("span").text(msg); } else if ("error" == status) { $(ele).parent().addClass("has-error"); $(ele).next("span").text(msg); } }
- 把抽取的方法应用到校验的位置
//校验表单数据 function validate_add_form() { //1.拿到要校验的数据,使用正则表达式 var empName = $("#empName_add_input").val();//通过id拿到值 var regName = /(^[a-zA-Z0-9_-]{6,16}$)|(^[\u2E80-\u9FFF]{2,5})/ /*alert(regName.test(empName));//先测试一下成功吗 return false;*/ /*if(!regName.test(empName)){ //1.如果校验失败 // alert("用户名可以是2-5位中文或者是2-16位英文和数字的组合");//美化替换他 用show_validate_msg(ele,status,msg)方法替换他 $("#empName_add_input").parent().addClass("has-error"); $("#empName_add_input").next("span").text("用户名可以是2-5位中文或者是2-16位英文和数字的组合"); return false; }else{ $("#empName_add_input").parent().addClass("has-success"); $("#empName_add_input").next("span").text(""); }*/ //用一个方法整合上面的 if (!regName.test(empName)) { show_validate_msg($("#empName_add_input"), "error", "用户名可以是2-5位中文或者是2-16位英文和数字的组合") } else { show_validate_msg($("#empName_add_input"), "success", "") } //2.如果没有失败,校验邮箱信息 var email = $("#email_add_input").val(); var regEmail = /^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/; /* if(!regEmail.test(email)){ //如果email校验失败了 // alert("邮箱格式不正确");//美化替换他 $("#email_add_input").parent().addClass("has-error"); $("#email_add_input").next("span").text("邮箱格式不正确"); return false; }else { $("#email_add_input").parent().addClass("has-success"); $("#enum_add_input").next("span").text(""); }*/ //用一个方法整合上面的 if (!regEmail.test(email)) { show_validate_msg("#email_add_input", "error", "邮箱格式不正确") return false; } else { show_validate_msg("#email_add_input", "success", "") } //一切都正常,返回true return true; }
这个时候,如果有两个相同名字的用户是可以注册成功的,这样就不对了
(5)ajax校验用户名是否重复
-
逻辑:把员工姓名输入完成后,就去给服务器发送一个ajax请求,服务器来告知我们这个用户名是否可用;如果可用,可用保存;如果不可用,提示错误信息
-
可以给员工姓名这一栏绑定一个change事件,当姓名改变时,做一些事情
//校验用户名是否可用 $("#empName_add_input").change(function () { //发送ajax请求校验用户名是否可用 var empName = this.value;//this.value是输入框的值 $.ajax({ url: "${APP_PATH}/checkSameEmployee", data: "empName=" + empName, type: "post", success: function (result) { if (result.code == 200) { show_validate_msg("#empName_add_input", "success", "用户名可用"); } else { show_validate_msg("#empName_add_input", "error", "用户名不可用"); } } }); });
-
当员工姓名改变时,发送ajax请求,校验用户名是否可用
-
在EmployeeController里面编写一个检验用户名是否可用的方法
/** * @param检查用户名是否可用 * @return Message * @description:检测用户是否存在 success表示用户不存在可用,fail表示用户存在不可用 * 可以通过识别状态码来分辨用户名可用不可用 */ @PostMapping(value = "/checkSameEmployee") @ResponseBody public Message checkSameEmployee(@RequestParam("empName") String empName) { boolean b = employeeService.checkSameEmloyee(empName); if(b){ return Msg.success(); }else{ return Msg.fail(); } }
-
在EmployeeService中创建相应的方法
/** * @param * @return boolean * @description 数据库中是否有empName,有返回false.没有返回true,true代表可用 */ public boolean checkSameEmployee(String empName) { EmployeeExample example = new EmployeeExample(); //创建查询条件 Criteria criteria = example.createCriteria(); //让员工的名字必须等于给定的值 criteria.andEmpNameEqualTo(empName); //返回给定信息的记录数 long count = employeeMapper.countByExample(example); //看返回值是否等于0(等于0,返回true,代表当前姓名可用;大于0返回false,不可用) return count == 0; }
选择输入数据库中已经有的用户名,会提示错误,如果这个时候邮箱输入正确,尽管用户名提示错误,还是可以保存成功
(6)完善用户名校验1
- ajax校验的信息保存起来:如果校验成功了,给保存按钮添加一个属性ajax-va;如果成功值就是success,如果失败值就是error
if (result.code == 200) { show_validate_msg("#empName_add_input", "success", "用户名可用"); //若果保存成功,就加一个属性,值为success $("#emp_save_btn").attr("ajax-va", "success"); } else { show_validate_msg("#empName_add_input", "error","用户名不可用"); $("#emp_save_btn").attr("ajax-va", "error"); }
- 点击保存按钮的时候,要先判断ajax校验用户名是否成功
这个时候可以完成要求了,但是添加成功后,再次点击新增的时候,输入框中是有数据的,这个时候再次添加还会添加成功
-
解决方法:点击新增时,清空输入框中的数据并且情况输入框的状态
-
写一个能够清空表单内容和样式的方法
//清空表单样式及内容 function reset_form(ele) { //重置表单内容 $(ele)[0].reset(); //清空表单样式 $(ele).find("*").removeClass("has-error has-success"); $(ele).find(".help-block").text(""); }
这个时候我们输入aaa这个用户名数据库中确实没有,提示可用,但是一点保存,就会提示不可用
(7)完善用户名校验2
- 检查用户名是否可用之前,判断用户名是否合法(用后端来校验)
- 对EmployeeController中的方法进行修改
@PostMapping(value = "/checkSameEmployee") @ResponseBody public Message checkSameEmployee(@RequestParam("empName") String empName) { //先判断用户名是否是合法的表达式 String regx = "(^[a-zA-Z0-9_-]{6,16}$)|(^[\\u2E80-\\u9FFF]{2,5})"; //看用户名是否匹配正则表达式 if(!empName.matches(regx)){//如果匹配失败 return Message.fail().add("va_msg","用户名必须是6-16位数字和字母的组合或者2-5位中文"); } //数据库用户名重复校验 boolean b = employeeService.checkSameEmployee(empName); if (b) {// 用户名不存在,可用 return Message.success(); } else { return Message.fail().add("va_msg","用户名不可用"); } }
- 失败后的提示消息也是在result中取
这样前端校验就算完成了,但是前端校验可以在网页中修改,所有重要的数据也要在后端校验
5.重要数据的后端校验(JSR303)
- 以前完成了jQuery前端校验,ajax用户名重复校验(前端校验防君子,不防小人,所以后端校验很有必要),重要数据要加上后端校验(JSR303);
- 重要的数据:前端校验+后端校验+数据库的约束
(1)想要实现JSR303的支持,必须要导入Hibernate-Validator包
<!-- hibernate-validator jsr303数据校验 -->
<!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-validator -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.3.5.Final</version>
</dependency>
(2)后端校验的实现
-
在bean包下的Employee类中,对需要后端校验的属性添加相应的注解
-
在Controller中,员工保存的方法,对相应的对象添加@Valid注解,用BindingResult result来封装校验的结果
-
如果校验成功,就提示成功,如果校验失败就返回失败
-
如果校验失败,返回失败的信息,在输入框的下方显示失败的信息(在result中提取失败的信息,把错误的信息都封装到map中)
@PostMapping(value = "/emp") @ResponseBody public Message saveEmployee(@Valid Employee employee, BindingResult result) { // System.out.println(employee); if (result.hasErrors()) {// 后端校验失败,返回校验失败的信息 Map<String, Object> map = new HashMap<>(); List<FieldError> errors = result.getFieldErrors(); for (FieldError error : errors) { System.out.println("错误的字段命名:"); map.put(error.getField(), error.getDefaultMessage()); } return Message.fail().add("errorField", map); } else { employeeService.saveEmployee(employee); return Message.success(); } }
-
到首页去完成逻辑的实现
//2.发送ajax请求保存员工 //jquery中提供了简单的把数据拿出来的方法(序列表格中的内容为字符串):serialize(),用于ajax请求,下面是个测试 /*alert($("#empAddModal form").serialize());*/ $.ajax({ url: "${APP_PATH}/emp", type: "post", data: $("#empAddModal form").serialize(), success: function (result) { //后端校验是否保存成功 if (result.code == "200") { //alert(result.msg); //员工保存成功后:1.关闭模态框; $("#empAddModal").modal('hide'); // 2.来到最后一页,显示刚才插入的数据 //发送ajax请求,显示最后一页(只要传一个足够大的数,分页插件都会默认到最后一页) //这里我吗用记录数,因为记录数,一定是大于页数的 to_page(totalRecord); } else { //显示失败信息 // consol`e.log(result); if (undefined != result.data.errorField.email) { //显示邮箱的错误信息 show_validate_msg("#email_add_input", "error", result.data.errorField.email); } if (undefined != result.data.errorField.empName) { show_validate_msg("#empName_add_input", "error", result.data.errorField.empName); } } } });
这样就完成了员工的新增功能
九、修改
0.逻辑
- 1.点击编辑
- 2.弹出用户修改的模态框(显示用户信息)
- 3.点击更新,完成用户修改
1.创建员工修改的模态框
- 把员工新增的模态框复制一份,用于员工修改的模态框
<!-- 员工修改的模态框 --> <div class="modal fade" id="empUpdateModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel"> <div class="modal-dialog" role="document"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</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 control-label">empName</label> <div class="col-sm-10"> <p class="form-control-static" id="empName_update_static"></p> </div> </div> <div class="form-group"> <label for="email_update_input" class="col-sm-2 control-label">email</label> <div class="col-sm-10"> <input type="text" name="email" class="form-control" id="email_update_input" placeholder="emil@qq.com"> <span class="help-block"></span> </div> </div> <div class="form-group"> <label class="col-sm-2 control-label">gender</label> <div class="col-sm-10"> <label class="radio-inline"> <input type="radio" name="gender" id="gender1_update_input" value="M" checked="checked"> 男 </label> <label class="radio-inline"> <input type="radio" name="gender" id="gender2_update_input" value="F"> 女 </label> </div> </div> <div class="form-group"> <label class="col-sm-2 control-label">deptName</label> <div class="col-sm-3"> <%--部门提交部门id--%> <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>
- 因为编辑按钮是通过ajax请求查出数据之后创建的,所以对他用加载完页面直接帮事件的方法行不通
- 为编辑绑事件的方法:1.创建完编辑按钮之后,为按钮绑事件;2.通过on方法(这里使用第二种)
- 之前查出的部门信息放到了员工添加的列表中,只需要把要添加的元素传进来即可
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3CqlEK4i-1574755610962)( https://raw.githubusercontent.com/danqing-hub/images/master/8d776b1ff71ab9c36a17c3b2b8a85a9b.png )] - 点击编辑,弹出模态框
/*修改 1.我们是按钮创建之前就绑定了click,所以绑不上 * 解决方法: * 1.可以在添加按钮的时候绑定 2.绑定live()方法,jequry新版没有live方法,可以用on替换 * */ $(document).on("click", ".edit_btn", function () { // alert("edit"); //查出员工信息,显示员工信息, getEmp($(this).attr("edit-id")); // 查出部门信息,并显示部门列表 getDepts("#dept_update_select"); //把员工的id传递给模态框的跟新按钮 $("#emp_update_btn").attr("edit-id",$(this).attr("edit-id")); //弹出模态框 $("#empUpdateModal").modal({ backdrop: "static" }); });
2.点击编辑,显示员工员工信息
-
把员工的姓名改成表单的静态控件(在bootstrap中查看)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ti0jWtyq-1574755610962)( https://raw.githubusercontent.com/danqing-hub/images/master/70c13b874485227965b9c30989a72dd9.png )] -
index中查询员工信息的方法
/** * 查出员工信息 * */ function getEmp(id) { $.ajax({ url: "${APP_PATH}/emp/" + id, type: "GET", success: function (result) { // console.log(result); var empData = result.data.employee; $("#empName_update_static").text(empData.empName);//给静态文本框赋值 $("#email_update_input").val(empData.email);//给input框赋值 //性别定位 $("#empUpdateModal input[name=gender]").val([empData.gender]); //部门定位 $("#empUpdateModal select").val([empData.dId]); } }); }
-
在EmployeeController中创建相应的方法
/**根据id查询员工 * @param * @return Message * @description:修改前将要修改的employee查询出来表单回显 */ @GetMapping(value = "/emp/{id}")//等价于@RequestMapping(value = "/emp/{id}",method=RequestMethod.GET) @ResponseBody public Message getEmployee(@PathVariable("id") Integer id) { Employee employee = employeeService.getEmployee(id); return Message.success().add("employee", employee); }
-
在EmployeeService中创建相应的方法
/** * 按照员工id查询员工 * @param id * @return */ public Employee getEmployee(Integer id) { Employee employee = employeeMapper.selectByPrimaryKey(id); return employee; }
-
可以在创建编辑按钮的时候,创建一个属性,来表示当前员工的id
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OcyaY6Ex-1574755610963)( https://raw.githubusercontent.com/danqing-hub/images/master/dd0c2d3c2bc4777860af55399b0a8459.png)]
3.点击修改,更新员工
-
为更新按钮绑定单击事件
//点击修改,更新员工信息 $("#emp_update_btn").click(function () { //1.验证邮箱是否合法 var email = $("#email_update_input").val(); var regEmail = /^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/; if (!regEmail.test(email)) { show_validate_msg("#email_update_input", "error", "邮箱格式不正确") return false; } else { show_validate_msg("#email_update_input", "success", "") } //2.发送ajax请求保存更新的员工数据 $.ajax({ url:"${APP_PATH}/emp/"+$(this).attr("edit-id"), type:"PUT", data:$("#empUpdateModal form").serialize(), success:function(result){ //alert(result.msg); //1.关闭模态框 $("#empUpdateModal").modal("hide"); //2.回到本页面 to_page(currentPage); } }); });
-
因为发送的是ajax请求,要写请求的处理事件
-
在EmployeeController中添加相应的保存方法
/**保存员工 * 遇到的问题:请求体中有数据: * 但是Employee对象封装不上; * SQL语句变成了 update tbl_emp where emp_id = 1014; * 原因: * Tomcat: * 1.将请求体中的数据,封装成一个map * 2.request.getParameter("empName")就会从这个map中取值。 * 3.SpringMvc封装pojo的时候会把pojo中的每个属性值,request.getParamter("email"); * Ajax发送PUT请求引发的血案 * PUT请求:请求体中的数据,request.getParameter("empName")拿不到的根本原因就是 * Tomcat一看是put请求,不会封装请求体中的数据为map,只有post形式的请求才封装请求体为map * @param * @return Message * @description:员工更新 这里ajax请求直接发put请求而不是post请求,那么所有的参数都会获取不到,因为tomcat只会封装post的数据。也就是说request.getParameter("empId")为空,springmvc也无法封装Bean * 解决方法: 1.发送post方法,通过HiddenHttpMethodFilter * 2.发送put请求,通过HttpPutFormContentFilter(通过web.xml配置) */ @PutMapping(value = "/emp/{empId}") @ResponseBody public Message saveUpdateEmployee(Employee employee) { System.out.println(employee); //logger.info(employee.toString()); employeeService.updateEmployee(employee); return Message.success(); }
-
在EmployeeService中添加相应的保存方法
java /** * 员工更新 * @param employee */ public void updateEmployee(Employee employee) { employeeMapper.updateByPrimaryKeySelective(employee);//根据主键有选择的更新 }
- 打开修改模态框的时候,把员工id的值,传给修改按钮
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d7Gy1axm-1574755610963)( https://raw.githubusercontent.com/danqing-hub/images/master/13df60982c95f219aeb9c9efca631d1d.png )]
- 打开修改模态框的时候,把员工id的值,传给修改按钮
-
因为tomcat不封装put请求发送的数据,spring提供了支持(HttpPutFormContentFilter
)
- 在web.xml中,把这个过滤器配置上
<filter> <filter-name>HttpPutFormContentFilter</filter-name> <filter-class>org.springframework.web.filter.HttpPutFormContentFilter</filter-class> </filter> <filter-mapping> <filter-name>HttpPutFormContentFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
- 在web.xml中,把这个过滤器配置上
-
更新成功之后关闭模态框,回到本界面
-
把员工的当前页码也记录一下
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cY5QC0bL-1574755610963)( https://raw.githubusercontent.com/danqing-hub/images/master/60c668d91ac15caf855ab99ac6e80727.png )]
这样就可以发送成功了
十、删除
1.单个删除
- 逻辑
URI:/emp/{id} DELETE形式的请求
(1)单个删除的步骤
-
在EmployeeController中拦截发送的请求
/** * 单个删除的方法 * @param id * @return */ @ResponseBody @RequestMapping(value = "/emp/{id}",method =RequestMethod.DELETE) public Message deleteEmpById(@PathVariable("id") Integer id){ employeeService.deleteEmployee(id); return Message.success(); }
-
在EmployeeService中创建相应的方法
/** * 员工删除 * @param empId */ public void deleteEmployee(Integer empId) { employeeMapper.deleteByPrimaryKey(empId); }
-
用on为删除按钮绑定单击事件
-
弹出是否确认删除的对话框(拿出员工的员工的名字)
-
删除成功后,返回当前页码
/** * 删除 * */ $(document).on("click",".delete_btn",function(){ //弹出是否确认删除对话框(找到当前的祖先节点的tr,tr下的第二个td的文本值) //alert($(this).parents("tr").find("td:eq(1)").text()); var empName = $(this).parents("tr").find("td:eq(2)").text(); var empId = $(this).attr("del-id");//要删除的员工id if(confirm("确认删除【"+empName+"】吗?")){ //点击确认发送ajax请求,删除即可 $.ajax({ url:"${APP_PATH}/emp/"+empId, type:"DELETE", success:function(result){ alert(result.msg); //回到本页 to_page(currentPage); } }); } });
-
构造删除按钮的时候给按钮添加一个自定义的属性来表示员工id
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9aIccMdO-1574755610964)( https://raw.githubusercontent.com/danqing-hub/images/master/01385226a4bdf3c069287e3d6a34754a.png )]
这样就完成了单个删除
2.批量删除
(1)添加多选框
-
给第一列第一行添加多选框
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Gfyr40ta-1574755610964)( https://raw.githubusercontent.com/danqing-hub/images/master/b0aca4497647fcc366cf7508fb02586d.png )] -
解析数据的时候也需要checkbox
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SBE6tJUr-1574755610964)( https://raw.githubusercontent.com/danqing-hub/images/master/622e9887a57929d0054d9647e3dd148a.png )] -
把CheckBox添加到遍历的元素中
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gKEqRb8l-1574755610965)( https://raw.githubusercontent.com/danqing-hub/images/master/ce2f1949f6e96014149594b5b584b694.png )]
(2) 完成全选全不选功能
- 点击最上面的按钮,让下面的按钮与最上面的按钮的checked的值保持同步
//完成全选,全不选功能 $("#check_all").click(function(){ //attr获取checked是undefined; //我们这些dom原生的属性:attr获取自定义属性的值,用prop修改和读取dom原生属性的值; $(this).prop("checked"); //让下面的选择框和第一个的值相同 $(".check_item").prop("checked",$(this).prop("checked")); });
- 当下面的选择框全部点满后,上面的选择框自动的选中
- 为下面的选择框添加单击事件
- 用checked选择器,查看被选中的个数
//给check_item绑定单击事件 $(document).on("click",".check_item",function(){ //判断当前选中的元素是否全选 var flag = ($(".check_item:checked").length == $(".check_item").length); $("#check_all").prop("checked",flag); });
(3)批量删除
-
给全部删除的按钮添加id
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DJbvG8wp-1574755610965)( https://raw.githubusercontent.com/danqing-hub/images/master/3ce6d9a888f9db12582a0438c5e43c39.png )] -
给删除按钮绑定单击事件
-
遍历被选中的员工,并提示
//点击全部删除,就批量删除 $("#emp_del_all_btn").click(function () { //遍历每一个被选中的,查出名字 var empNames = ""; var del_idstr = ""; $.each($(".check_item:checked"),function(){ empNames += $(this).parents("tr").find("td:eq(2)").text()+","; //组装员工id字符串 del_idstr += $(this).parents("tr").find("td:eq(1)").text()+"-"; }); //去除多余的(最后一个)逗号:从0开始截取字符串,到导数第二个 empNames = empNames.substring(0,empNames.length-1); //去除多余的(最后一个)-:从0开始截取字符串,到导数第二个 del_idstr = del_idstr.substring(0,del_idstr.length-1); if(confirm("确认删除【"+empNames+"】员工吗?")){ //发送ajax请求删除 $.ajax({ url:"${APP_PATH}/emp/"+del_idstr, type:"delete", success:function(result){ alert(result.msg); //回到当前页面 to_page(currentPage); } }); } });
-
把原来的方法改成单个批量二合一的方法
/*** * 删除多个员工(批量) * @param ids */ public void deleteBatch(List<Integer> ids) { EmployeeExample example = new EmployeeExample(); Criteria criteria = example.createCriteria(); //delete from xxx where emp_id in(1,2,3) criteria.andEmpIdIn(ids); employeeMapper.deleteByExample(example); }
-
在EmployeeService中创建可以同时删除多个的方法
/** * @param * @return Message * @description:单个批量删除 单个删除:1 批量删除:1-2-3 */ @DeleteMapping(value = "/emp/{ids}") @ResponseBody public Message deleteEmployee(@PathVariable("ids") String ids) { if (ids.contains("-")) {//包含-就是批量删除,调用方法如下 String[] str_ids = ids.split("-");//分割成数组 List<Integer> del_ids = new ArrayList<>(); for (String id : str_ids) { del_ids.add(Integer.parseInt(id)); } employeeService.deleteBatch(del_ids); } else {//不包含-就是单个删除,调用方法如下: Integer id = Integer.parseInt(ids); employeeService.deleteEmployee(id); } return Message.success(); }
//组装员工id字符串
del_idstr += $(this).parents("tr").find("td:eq(1)").text()+"-";
});
//去除多余的(最后一个)逗号:从0开始截取字符串,到导数第二个
empNames = empNames.substring(0,empNames.length-1);
//去除多余的(最后一个)-:从0开始截取字符串,到导数第二个
del_idstr = del_idstr.substring(0,del_idstr.length-1);
if(confirm("确认删除【"+empNames+"】员工吗?")){
//发送ajax请求删除
$.ajax({
url:"${APP_PATH}/emp/"+del_idstr,
type:"delete",
success:function(result){
alert(result.msg);
//回到当前页面
to_page(currentPage);
}
});
}
});
```
- 把原来的方法改成单个批量二合一的方法
/*** * 删除多个员工(批量) * @param ids */ public void deleteBatch(List<Integer> ids) { EmployeeExample example = new EmployeeExample(); Criteria criteria = example.createCriteria(); //delete from xxx where emp_id in(1,2,3) criteria.andEmpIdIn(ids); employeeMapper.deleteByExample(example); }
- 在EmployeeService中创建可以同时删除多个的方法
/** * @param * @return Message * @description:单个批量删除 单个删除:1 批量删除:1-2-3 */ @DeleteMapping(value = "/emp/{ids}") @ResponseBody public Message deleteEmployee(@PathVariable("ids") String ids) { if (ids.contains("-")) {//包含-就是批量删除,调用方法如下 String[] str_ids = ids.split("-");//分割成数组 List<Integer> del_ids = new ArrayList<>(); for (String id : str_ids) { del_ids.add(Integer.parseInt(id)); } employeeService.deleteBatch(del_ids); } else {//不包含-就是单个删除,调用方法如下: Integer id = Integer.parseInt(ids); employeeService.deleteEmployee(id); } return Message.success(); }
恭喜你:这样ssm框架的crud项目就完成了!!!