文章目录
1.主要功能点:
分页
数据校验(前端Jquery+后端JSR303)
ajax异步请求
Rest风格URL;使用http协议请求方式,表示对资源的操作
GET(查询)
POST(添加)
PUT(修改)
DELETE(删除)
2.技术点:
JDK:1.8
基础框架:Spring+SpringMVC+mybatis
前端页面框架:bootstrap
服务器:tomcat8
数据库:Mysql
数据库连接池:druid
依赖管理:Maven
mybatis分页插件:pagehelper
一、基础环境搭建
1. 1.创建maven的web工程
1. 2 引入项目依赖的jar包
在pom.xml中的properties配置中统一管理相关版本
<spring.version>5.2.6.RELEASE</spring.version>
<mybatis.version>3.4.6</mybatis.version>
<mysql.version>5.1.46</mysql.version>
spring与springmvc
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
mybaits及与spring整合包
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.2</version>
</dependency>
数据库连接池以及数据库驱动
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
其他(jstl,jsp,servlet-api,junit,AOP相应操作)
<!-- https://mvnrepository.com/artifact/jstl/jstl -->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
1.3 引入bootstrap前端框架
在jsp页面中引入:
<%-- 引入jquery--%>
<link href="./static/bootstrap-3.3.7-dist/css/bootstrap.min.css" rel="stylesheet">
<script type="text/javascript" src="/static/js/jquery-1.7.2.js"></script>
<%-- 引入样式--%>
<script src="./static/bootstrap-3.3.7-dist/js/bootstrap.min.js"></script>
1.4 编写整合的配置文件
1.4.1 web.xml
<!-- 启动Spring的容器-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- 解决中文乱码的过滤器,一定要放在所有过滤器之前-->
<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>
</filter>
<!-- 使用Rest风格的URL,将页面普通的POST请求转为指定的delete或者put请求-->
<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-mapping>
<filter-name>hiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 配置springMVC的前端控制器-->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 配置初始参数,服务器一启动就加载springmvc的配置-->
<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>
1.4.2 springmvc.xml(包含网站页面的跳转逻辑控制配置)
<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
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.js" use-default-filters="false">
<!-- 只扫描控制器-->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- 配置视图解析器,方便页面返回解析-->
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<!-- 开启mvc注解扫描以及servlet-handler-->
<!-- 将springmvc不能处理的请求交给tomcat-->
<mvc:default-servlet-handler></mvc:default-servlet-handler>
<!-- 支持mvc更高级的一些功能,JSR303校验,ajax请求以及映射动态请求-->
<mvc:annotation-driven></mvc:annotation-driven>
</beans>
1.4.3 application.xml(Spring相关配置)
创建德鲁伊连接池的配置文件:
配置spring文件:
<?xml version="1.0" encoding="UTF-8"?>
<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:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
">
<!-- Spring的配置文件,主要配置和业务逻辑相关的-->
<context:component-scan base-package="com.js">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--引入数据源-->
<context:property-placeholder location="classpath:dbdruid.properties"></context:property-placeholder>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driver" value="${driver}"></property>
<property name="url" value="${url}"></property>
<property name="username" value="${username}"></property>
<property name="password" value="${password}"></property>
</bean>
<!-- 与mybatis整合,可以生成sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="configLocation" value="mybatis-config"></property>
<property name="dataSource" ref="dataSource"></property>
<!-- 指定mybatis的mapper文件位置-->
<property name="mapperLocations" value="classpath:mapper/*.xml"></property>
</bean>
<!-- 配置扫描器,将mybatis接口的实现加入到IOC容器中-->
<bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 扫描所有DAO接口的实现,加入IOC容器-->
<property name="basePackage" value="com.js.dao"></property>
</bean>
<!-- 事务管理控制配置,创建事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 指定需要控制的数据源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 开启事务注解驱动-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
</beans>
1.4.4 mybatis的基本配置文件(参考官方文档)
二、创建基础bean,DAO(mapper)增删改查逻辑及测试具体步骤
2.1创建所需要的表(mysql)
CREATE DATABASE ssm
USE smm
CREATE TABLE emp(
emp_id INT(11) PRIMARY KEY NOT NULL AUTO_INCREMENT,
emp_name VARCHAR(200) NOT NULL,
gender CHAR(1),
email VARCHAR(200)
)
CREATE TABLE dept(
dept_id INT(11) PRIMARY KEY NOT NULL AUTO_INCREMENT,
dept_name VARCHAR(200) NOT NULL
)
#添加列
ALTER TABLE emp
ADD d_id INT(11)
#添加外键约束
ALTER TABLE emp
ADD CONSTRAINT fk_emp_dept FOREIGN KEY(d_id) REFERENCES dept(dept_id)
2.2使用mybatis逆向工程生成对应的bean及mapper(也可自己手动根据表进行书写相应bean及mapper)
2.2.1 引入mybatis Generator core.jar包
<!-- 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.2.2 书写相应的xml配置文件,参考mybatis Generator官方文档
<?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">
<!-- 数据库连接配置-->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/ssm"
userId="root"
password="123456">
</jdbcConnection>
<javaTypeResolver>
<property name="forceBigDecimals" value="false"/>
</javaTypeResolver>
<!--指定javabean生成的位置-->
<javaModelGenerator
targetPackage="com.js.bean"
targetProject=".\src\main\java">
<property name="enableSubPackages" value="true"/>
<property name="trimStrings" value="true"/>
</javaModelGenerator>
<!-- 指定sql映射文件生成的位置-->
<sqlMapGenerator
targetPackage="mapper"
targetProject=".\src\main\resources">
<property name="enableSubPackages" value="true"/>
</sqlMapGenerator>
<!--指定dao接口生成的位置-->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.js.dao"
targetProject=".\src\main\java">
<property name="enableSubPackages" value="true"/>
</javaClientGenerator>
<!--指定每个表的生成策略-->
<table tableName="emp" domainObjectName="Employee"></table>
<table tableName="dept" domainObjectName="Department"></table>
</context>
</generatorConfiguration>
2.2.3 利用官方java代码运行generator
public class MBGTest {
public static void main(String[] args) throws IOException, XMLParserException, InvalidConfigurationException, SQLException, InterruptedException {
List<String> warnings = new ArrayList<String>();
boolean overwrite = true;
File configFile = new File("E:\\workspace_idea1\\SSMproject\\ssm_crud\\src\\main\\resources\\mbg.xml");
System.out.println(configFile.getAbsolutePath());
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);
}
}
为防止找不到XML文件,加载文件时使用了绝对路径。
运行完成将生成对应的bean,mapper接口以及相应的mapper.xml文件
mapper接口提供了增删改查的方法
mapper.xml文件中则包含了基本的增删改查操作。
2.3 根据业务逻辑修改自动生成的mapper中的文件
新增查询员工时能同时查到其对应部门的信息,步骤:
在员工接口中新增方法
List<Employee> selectByExampleWithDept(EmployeeExample example);
Employee selectByPrimaryKeyWithDept(Integer empId);
在员工类中添加部门类的属性,表示员工属于的部门信息,并生成对应get与set方法
private Department department;
在员工mapper.xml中添加新增方法的配置
<!--新增方法
List<Employee> selectByExampleWithDept(EmployeeExample example);
Employee selectByPrimaryKeyWithDept(Integer empId);-->
<select id="selectByExampleWithDept" resultMap="WithDeptResultMap">
select
<if test="distinct">
distinct
</if>
<include refid="WithDept_Column_List" />
from emp left join dept on emp.d_id=dept.dept_id
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
<if test="orderByClause != null">
order by ${orderByClause}
</if>
</select>
<select id="selectByPrimaryKeyWithDept" resultMap="WithDeptResultMap">
select
<include refid="WithDept_Column_List" />
from emp left join dept on emp.d_id=dept.dept_id
where emp_id = #{empId,jdbcType=INTEGER}
</select>
新增方法中,列不再使用基本列,使用了带部门信息的新增列。因此要新增列的sql定义:
<!-- 新增sql-->
<sql id="WithDept_Column_List">
emp_id, emp_name, gender, email, d_id,dept_id,dept_name
</sql>
返回的resultMap也与基本定义的不一致,因此要新增resultMap定义:
<resultMap id="WithDeptResultMap" type="com.js.bean.Employee">
<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="department" javaType="com.js.bean.Department">
<id column="dept_id" property="deptId"/>
<id column="dept_name" property="deptName"/>
</association>
</resultMap>
2.4 写业务层前,测试数据库层mapper
2.4.1以前的测试方式
public class DepartmentMapperTest {
@Test
public void testCRUD(){
//1.创建Spring容器
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
//2.从容器中取得mapper
DepartmentMapper mapper = context.getBean(DepartmentMapper.class);
//3.利用mapper操作
}
}
2.4.2 使用Spring单元测试,可以自动注入需要的组件
导入需要的依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
测试实例:
/**
ContextConfiguration:指定Spring配置文件的位置
直接autowired我们要使用的组件即可
*/
@ContextConfiguration(locations = {"classpath*:applicationContext.xml"})
@RunWith(SpringJUnit4ClassRunner.class)
public class DepartmentMapperTest {
@Autowired
DepartmentMapper departmentMapper;
@Test
public void testCRUD(){
System.out.println(departmentMapper);
}
}
结果:拿到了DepartmentMapper接口的代理对象
同理可创建员工测试MapperTest,拿到员工代理对象。
插入测试
departmentMapper.insertSelective(new Department(null,"开发部"));
departmentMapper.insertSelective(new Department(null,"测试部"));
结果:
※注:进行批量插入(员工):使用可以执行批量操作的sqlSession
配置一个可以执行批量的sqlSession:在Spring配置中配置
<!-- 配置可以执行批量的sqlSession,没有无参构造器,BATCH可以积攒sql语句-->
<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>
配置完成后,便可以在员工Mapper测试类进行自动注入:
@ContextConfiguration(locations = {"classpath*:applicationContext.xml"})
@RunWith(SpringJUnit4ClassRunner.class)
public class EmployeeMapperTest {
@Autowired
EmployeeMapper employeeMapper;
@Autowired
SqlSession sqlSession;
批量插入:
@Test
public void TestCRUD(){
System.out.println(employeeMapper);
employeeMapper.insert(new Employee(null,"Tom","M","Tom@qq.com",1));
// 实现批量插入,使用可以执行批量操作的sqlSession
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
for (int i=0;i<100;i++){
//使用UUID拼接员工姓名
String uid = UUID.randomUUID().toString().substring(0, 5)+i;
mapper.insert(new Employee(null,uid,"M",uid+"@qq.com",1));
}
}
三、结合web服务器实现页面,包括了CRUD操作的页面展示
3.1 查询
3.1.1访问index.jsp页面
我们可以规定查询的url为:/emps
那么在index.jsp中:规定跳转页面
<jsp:forward page="/emps"></jsp:forward>
这会在控制器中查找对应路径的方法进行执行。
3.1.2 index页面发送查询员工列表的http请求
3.1.3EmployeeController控制器接受请求,查出员工数据
创建EmployeeController控制器类,处理员工增删改查处理,并创建相应的service类,利用service对象调用方法进行查询。而在service层中,必然会调用DAO,在service对象中进行DAO的注入。
Service对象创建:
@Service
public class EmployeeService {
@Autowired
EmployeeMapper employeeMapper;
//查询所有员工,没有查询条件,因此参数为null
public List<Employee> getAll() {
return employeeMapper.selectByExampleWithDept(null);
}
}
Controller对象创建:
@Controller
public class EmployeeController {
@Autowired
EmployeeService employeeService;
/**
* @param
* @return java.lang.String
* @methodname getEmps
* @Description 查询员工数据
* @Date 2020/11/12 21:32
**/
//方法上的RequestMapping注解的path表示了访问的路径
@RequestMapping(path = "/emps")
public String getEmps() {
List<Employee> list=employeeService.getAll();
return "list";
}
}
**注:****这是未加分页的情况,查询的数据可能非常多。因此要进行分页处理
*****进行分页处理
请求参数中加入整形数据pn表示页面,不传表示默认值。在控制器方法中获取参数,在查询时候加入limit限制。(具体通过引入mybatis的pagehelper插件进行)。
①引入依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.10</version>
</dependency>
②在mybatis全局配置中注册插件
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
③在service中查询方法进行调用,并利用Model添加结果,可以带给页面
@Controller
public class EmployeeController {
@Autowired
EmployeeService employeeService;
/**
* @param
* @return java.lang.String
* @methodname getEmps
* @Description 查询员工数据
* @Date 2020/11/12 21:32
**/
//方法上的RequestMapping注解的path表示了访问的路径
@RequestMapping(path = "/emps")
public String getEmps(@RequestParam(value = "pn",defaultValue = "1") Integer pn, Model model) {
//引入PageHelper分页插件,在查询之前只需要调用,传入页码及每页数据大小
PageHelper.startPage(pn,5);
//startPage后面紧跟的查询就是一个分页查询
List<Employee> list=employeeService.getAll();
//使用PageInfo类对查询结果进行包装,4表示页面上要连续显示几页
// pageInfo包装后,数据个数,页码数等信息都能找到,对于页面展示很有帮助
PageInfo page=new PageInfo(list,4);
model.addAttribute("pageInfo",page);
return "list";
}
}
*****编写MockMvc测试类虚拟模拟mvc请求进行测试,获取处理结果(@ContextConfiguration的加载文件需要加载spring和springmvc的配置文件)
注:@Before引入的是junit下的:import org.junit.Before;
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(locations = {"classpath*:applicationContext.xml","classpath*:springmvc.xml"})
public class MCVTest {
//传入SpringMvc的IOC
@Autowired
WebApplicationContext context;
//虚拟的mvc请求,获取处理结果
MockMvc mockMvc;
@Before
public void initMockMvc(){
mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
}
@Test
public void testPage() throws Exception {
//模拟请求拿到返回值,MockMvcRequestBuilders的后面可以是get,post,put,delete等请求
MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get("/emps").param("pn", "1")).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 num : nums) {
System.out.print(" "+num);
}
List<Employee> list = pageInfo.getList();
for (Employee employee : list) {
System.out.println("员工ID:"+employee.getEmpId());
}
}
}
成功获取结果:
3.1.4跳转到list.jsp页面进行展示
在list页面中取出数据,进行显示,进行与首页相同的CSS样式,JS引入,基于Bootstrap搭建页面。
基本页面框架搭建:
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<%--引入标签库--%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
<title>员工列表</title>
<%
pageContext.setAttribute("APP_PATH", request.getContextPath());
%>
<%-- 引入jquery--%>
<link href="./static/bootstrap-3.3.7-dist/css/bootstrap.min.css" rel="stylesheet">
<script type="text/javascript" src="${APP_PATH}/static/js/jquery-1.7.2.js"></script>
<%-- 引入样式--%>
<script src="${APP_PATH}/static/bootstrap-3.3.7-dist/js/bootstrap.min.js"></script>
</head>
<body>
<%--搭建页面--%>
<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 type="button" class="btn btn-info">新增</button>
<button type="button" class="btn btn-danger">删除</button>
</div>
</div>
<%-- 显示表格--%>
<div class="row">
<div class="col-md-12">
<table class="table table-hover">
<tr>
<th>empId</th>
<th>empName</th>
<th>gender</th>
<th>email</th>
<th>deptName</th>
<th>操作</th>
</tr>
<c:forEach items="${pageInfo.list}" var="emp">
<tr>
<td>${emp.empId}</td>
<td>${emp.empName}</td>
<td>${emp.gender}</td>
<td>${emp.email}</td>
<td>${emp.department.deptName}</td>
<td>
<button type="button" class="btn btn-info btn-sm">
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span>
编辑
</button>
<button type="button" class="btn btn-danger btn-sm">
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span>
删除
</button>
</td>
</tr>
</c:forEach>
</table>
</div>
</div>
<%-- 显示分页--%>
<div class="row">
<%-- 分页文字信息--%>
<div class="col-md-6">
当前页面:${pageInfo.pageNum} 总页码:${pageInfo.pages} 共${pageInfo.total}条记录
</div>
<%-- 分页条信息--%>
<div class="col-md-6">
<nav aria-label="Page navigation">
<ul class="pagination">
<li><a href="${APP_PATH}/emps">首页</a></li>
<c:if test="${pageInfo.hasPreviousPage}">
<li>
<a href="${APP_PATH}/emps?pn=${pageInfo.pageNum-1}" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
</c:if>
<c:forEach items="${pageInfo.navigatepageNums}" var="page_Num">
<c:if test="${page_Num==pageInfo.pageNum}">
<li class="active"><a href="#">${page_Num}</a></li>
</c:if>
<c:if test="${page_Num!=pageInfo.pageNum}">
<li><a href="${APP_PATH}/emps?pn=${page_Num}">${page_Num}</a></li>
</c:if>
</c:forEach>
<c:if test="${pageInfo.hasNextPage}">
<li>
<a href="${APP_PATH}/emps?pn=${pageInfo.pageNum+1}" 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>
</div>
</div>
</div>
</body>
</html>
页面效果展示:
3.1.5优化改进查询:服务器端以json格式发送信息发送给客户端,由客户端解析json获取数据,不再受限于浏览器,也可由安卓,IOS进行访问(使用ajax)。
步骤:
1.index,jsp页面直接发送ajax请求进行员工分页查询
2.服务器端查出数据,以json形式返回给浏览器
3.浏览器收到json字符串,使用js对json解析,使用js通过dom增删改改变页面
导入依赖jar包:处理json
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.2</version>
</dependency>
bean中书写一个通用的操作通知信息的类Msg:可用于通知操作成功与失败
public class Msg {
//表示状态码
private int code;
//提示信息
private String msg;
//用户要返回给浏览器的信息,封装在map中
private Map<String,Object> extend=new HashMap<String,Object>();
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;
}
}
重新提供Controller中的方法:不再需要以Model类储存
@RequestMapping("/emps")
@ResponseBody
public Msg getEmpsWithjson(@RequestParam(value = "pn",defaultValue = "1") Integer pn){
//引入PageHelper分页插件,在查询之前只需要调用,传入页码及每页数据大小
PageHelper.startPage(pn,5);
//startPage后面紧跟的查询就是一个分页查询
List<Employee> list=employeeService.getAll();
//使用PageInfo类对查询结果进行包装,4表示页面上要连续显示几页
// pageInfo包装后,数据个数,页码数等信息都能找到,对于页面展示很有帮助
PageInfo page=new PageInfo(list,4);
return Msg.success().add("PageInfo",page);
}
}
重新书写index页面进行显示:利用ajax发送请求:
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<%--引入标签库--%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
<title>员工列表</title>
<%
pageContext.setAttribute("APP_PATH", request.getContextPath());
%>
<%-- 引入jquery--%>
<link href="./static/bootstrap-3.3.7-dist/css/bootstrap.min.css" rel="stylesheet">
<script type="text/javascript" src="${APP_PATH}/static/js/jquery-1.7.2.js"></script>
<%-- 引入样式--%>
<script src="${APP_PATH}/static/bootstrap-3.3.7-dist/js/bootstrap.min.js"></script>
</head>
<body>
<%--搭建页面--%>
<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 type="button" class="btn btn-info">新增</button>
<button type="button" class="btn btn-danger">删除</button>
</div>
</div>
<%-- 显示表格--%>
<div class="row">
<div class="col-md-12">
<table class="table table-hover" id="emps_table">
<thead>
<tr>
<th>empId</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>
<%--页面加载完成以后,直接去发送ajax请求,要到分页数据--%>
<script type="text/javascript">
$(function () {
to_page(1);
});
function to_page(pn) {
$.ajax({
url: "${APP_PATH}/emps",
data: "pn="+pn,
type: "get",
success: function (result) {
// console.log(result);
// 1.解析并显示员工数据
build_emps_table(result);
// 2.解析并显示分页信息
bulid_page_info(result);
//解析显示分页条信息
build_page_nav(result);
//跳转页面
}
});
}
function build_emps_table(result) {
//清空table表
$("#emps_table tbody").empty();
var emps = result.extend.pageInfo.list;
$.each(emps, function (index, item) {
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);
var editBtn=$("<button></button>").addClass("btn btn-info btn-sm").append($("<span></span>").addClass("glyphicon glyphicon-pencil")).append("编辑");
var delBtn=$("<button></button>").addClass("btn btn-danger btn-sm").append($("<span></span>").addClass("glyphicon glyphicon-trash")).append("删除");
var BtnTd=$("<td></td>").append(editBtn).append(" ").append(delBtn);
// 使用append进行添加
$("<tr></tr>").append(empIdTd).append(empNameTd).append(genderTd).append(emailTd)
.append(deptNameTd).append(BtnTd).appendTo("#emps_table tbody");
});
}
// 解析显示分页信息
function bulid_page_info(result) {
$("#page_info_area").empty();
$("#page_info_area").append("当前页面: "+result.extend.pageInfo.pageNum+"总页码:"+result.extend.pageInfo.pages+"共"+result.extend.pageInfo.total+"条记录")
}
// 解析显示分页条
function build_page_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("«").attr("href","#"));
if (result.extend.pageInfo.hasPreviousPage==false){
firstPageLi.addClass("disabled");
prePageLi.addClass("disabled");
}else {
firstPageLi.click(function () {
to_page(1);
})
prePageLi.click(function () {
to_page(result.extend.pageInfo.pageNum-1);
})
}
ul.append(firstPageLi).append(prePageLi);
$.each(result.extend.pageInfo.navigatepageNums,function (index,item) {
var numLi=$("<li></li>").append($("<a></a>").append(item).attr("href","#"));
if (result.extend.pageInfo.pageNum==item){
numLi.addClass("active");
}
numLi.click(function () {
to_page(item);
})
ul.append(numLi);
})
var nextPageLi=$("<li></li>").append($("<a></a>").append("»").attr("href","#"));
var lastPageLi=$("<li></li>").append($("<a></a>").append("末页").attr("href","#"));
if (result.extend.pageInfo.hasNextPage==false){
nextPageLi.addClass("disabled");
lastPageLi.addClass("disabled");
}else {
lastPageLi.click(function () {
to_page(result.extend.pageInfo.pages);
})
nextPageLi.click(function () {
to_page(result.extend.pageInfo.pageNum+1);
})
}
ul.append(nextPageLi).append(lastPageLi);
var navEle=$("<nav></nav>").append(ul);
$("#page_nav_area").append(navEle);
}
</script>
</body>
</html>
3.2 新增(添加员工)
1.index,jsp页面点击新增2.弹出新增对话框(即bootstrap的js模态框)
3.查询数据库部门列表,显示在对话框中
4.用户输出数据完成保存
注:REST风格,规定URL,发送不同类型请求
/emp/{id} GET 查询员工
/emp/ POST 保存员工
/emp/{id} PUT 修改员工
/emp/{id} DELETE 删除员工
3.2.1服务器端逻辑
在EmployeeCtroller类中新增方法,并在相应service增加方法和调用mapper的方法。同时基于注解在service层的增删改方法上添加事务。
EmployeeCtroller:
//员工保存
@RequestMapping(path="/emps",method = RequestMethod.POST)
@ResponseBody
public Msg saveEmp(Employee employee){
employeeService.saveEmp(employee);
return Msg.success();
}
service:
@Transactional(propagation = Propagation.REQUIRES_NEW,isolation = Isolation.REPEATABLE_READ,rollbackFor = Exception.class)
public void saveEmp(Employee employee) {
employeeMapper.insertSelective(employee);
}
3.2.1增加输入校验规则(具体参考前端页面中的书写)
此外:①增加ajax发送请求验证用户名是否可用:
Controller层:
//员工名称校验是否可用
@RequestMapping(path="/checkuser",method = RequestMethod.POST)
@ResponseBody
public Msg checkUser(String empName){
//先判断用户名是否是合法表达式
String regx="(^[a-zA-Z0-9_-]{6,16}$)|(^[\u2E80-\u9FFF]{2,5})";
boolean matches = empName.matches(regx);
if (!matches){
return Msg.fail().add("va_msg","用户名必须是6-16数字和字母组合或2-5位中文");
}
//数据库用户名校验
boolean result=employeeService.checkUser(empName);
if(result){
return Msg.success();
}
return Msg.fail().add("va_msg","用户名不可用");
}
Servive层:返回true表示可用,false表示用户名已存在
public boolean checkUser(String empName) {
EmployeeExample example= new EmployeeExample();
EmployeeExample.Criteria criteria = example.createCriteria();
criteria.andEmpNameEqualTo(empName);
long count = employeeMapper.countByExample(example);
return count==0;
}
②新增JSR303后端校验:
导入jar包:
<!-- https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator -->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.1.5.Final</version>
</dependency>
在员工类中利用注解:
@Pattern(regexp ="(^[a-zA-Z0-9_-]{6,16}$)|(^[\\u2E80-\\u9FFF]{2,5})",
message = "用户名必须是6-16数字和字母组合或2-5位中文")
private String empName;
@Pattern(regexp = "^([a-z0-9_\\.-]+)@([\\da-z\\.-]+)\\.([a-z\\.]{2,6})$",
message = "邮箱格式不合法")
private String email;
如何校验:在controller层封装对象时添加valid注解:BindingResult用于判断封装结果
//员工保存
@RequestMapping(path="/emps",method = RequestMethod.POST)
@ResponseBody
public Msg saveEmp(@Valid Employee employee, BindingResult result){
if (result.hasErrors()){
//校验失败,返回失败,在模态框中显示校验失败的错误信息
Map<String,Object> map=new HashMap<String, Object>();
List<FieldError> fieldErrors = result.getFieldErrors();
for (FieldError error : fieldErrors) {
System.out.println("错误的字段名:"+error.getField());
System.out.println("错误信息:"+error.getDefaultMessage());
map.put(error.getField(),error.getDefaultMessage());
}
return Msg.fail().add("errorFields",map);
}else {
employeeService.saveEmp(employee);
return Msg.success();
}
}
3.3 修改
与新增类似,利用模态框输入参数,点击保存上传参数给服务器,访问数据库进行修改。
tomcat服务器发送put请求:
方案一:利用POST的type,在ajax访问参数中加入_method.在tomcat服务器中,只有POST请求会进行数据封装。
$.ajax({
url:"${APP_PATH}/emps/"+$(this).attr("edit_id"),
type:"POST",
data:$("#empUpdateModel form").serialize()+"&_method=PUT",
success:function (result) {
alert(result.msg);
}
});
方案二:配置filter(不必再传递_method参数,type直接使用PUT),FormContentFilter,可以处理PUT和DELETE请求
<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>
Controller及Servive的方法:
//更新员工
@RequestMapping(path="/emps/{empId}",method = RequestMethod.PUT)
@ResponseBody
public Msg saveEmp(Employee employee){
employeeService.updateEmp(employee);
return Msg.success();
}
//员工更新
@Transactional(propagation = Propagation.REQUIRES_NEW,isolation = Isolation.REPEATABLE_READ,rollbackFor = Exception.class)
public void updateEmp(Employee employee) {
employeeMapper.updateByPrimaryKeySelective(employee);
}
3.4 删除
3.4 .1单个删除
请求的URL:"/emps/{id}"。
//删除员工byId
@RequestMapping(path="/emps/{id}",method = RequestMethod.DELETE)
@ResponseBody
public Msg deleteEmpById(@PathVariable(name = "id") Integer id){
employeeService.deleteEmpById(id);
return Msg.success();
}
//员工删除
@Transactional(propagation = Propagation.REQUIRES_NEW,isolation = Isolation.REPEATABLE_READ,rollbackFor = Exception.class)
public void deleteEmpById(Integer id) {
employeeMapper.deleteByPrimaryKey(id);
}
3.4.2批量删除
contrlloer层取消单个删除的方法,进行整合:
//批量删除员工
@RequestMapping(path="/emps/{ids}",method = RequestMethod.DELETE)
@ResponseBody
public Msg deleteEmp(@PathVariable(name = "ids") String ids){
String[] str_ids= ids.split("-");
if (str_ids.length==1){
employeeService.deleteEmpById(Integer.parseInt(str_ids[0]));
}else{
ArrayList<Integer> in_ids = new ArrayList<>();
for (String id : str_ids) {
in_ids.add(Integer.parseInt(id));
}
employeeService.deleteEmpBatch(in_ids);
}
return Msg.success();
}
Service层
//批量删除
@Transactional(propagation = Propagation.REQUIRES_NEW,isolation = Isolation.REPEATABLE_READ,rollbackFor = Exception.class)
public void deleteEmpBatch(List<Integer> ids) {
EmployeeExample example = new EmployeeExample();
EmployeeExample.Criteria criteria = example.createCriteria();
//delete from XXX where emp_id in XXX
criteria.andEmpIdIn(ids);
employeeMapper.deleteByExample(example);
}
四、页面jsp文件
index.jsp:
<%--
Created by IntelliJ IDEA.
User: js
Date: 2020/11/11
Time: 23:05
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<%--引入标签库--%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
<title>员工列表</title>
<%
pageContext.setAttribute("APP_PATH", request.getContextPath());
%>
<%-- 引入jquery--%>
<script src="${APP_PATH}/static/js/jquery-1.12.4.min.js"></script>
<link href="./static/bootstrap-3.3.7-dist/css/bootstrap.min.css" rel="stylesheet">
<%-- 引入样式--%>
<script src="${APP_PATH}/static/bootstrap-3.3.7-dist/js/bootstrap.min.js"></script>
</head>
<body>
<!-- 员工修改的模态框 -->
<div class="modal fade" id="empUpdateModel" 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="myModalLabe">员工修改</h4>
</div>
<div class="modal-body">
<form class="form-horizontal">
<div class="form-group">
<label class="col-sm-2 control-label">empName</label>
<div class="col-sm-10">
<p class="form-control-static" id="empName_upadate_static"></p>
</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"> 男
</label>
<label class="radio-inline">
<input type="radio" name="gender" id="gender2_update_input" value="F"> 女
</label>
</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" class="form-control" name="email" id="email_update_input"
placeholder="email@qq.com">
<span class="help-block"></span>
</div>
</div>
<div class="form-group">
<label for="email_add_input" class="col-sm-2 control-label">deptName</label>
<div class="col-sm-4">
<%-- 部门提交部门ID即可--%>
<select class="form-control" name="dId" id="dept_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" 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" class="form-control" name="empName" id="empName_add_input"
placeholder="empName">
<span class="help-block"></span>
</div>
</div>
<div class="form-group">
<label for="empName_add_input" 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"> 男
</label>
<label class="radio-inline">
<input type="radio" name="gender" id="gender2_add_input" value="F"> 女
</label>
</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" class="form-control" name="email" id="email_add_input"
placeholder="email@qq.com">
<span class="help-block"></span>
</div>
</div>
<div class="form-group">
<label for="email_add_input" class="col-sm-2 control-label">deptName</label>
<div class="col-sm-4">
<%-- 部门提交部门ID即可--%>
<select class="form-control" name="dId" id="dept_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 type="button" class="btn btn-info" id="emp_add_model_btn">新增</button>
<button type="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>empId</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;
$(function () {
to_page(1);
});
//跳转转页面
function to_page(pn) {
$.ajax({
url: "${APP_PATH}/emps",
data: "pn=" + pn,
type: "get",
success: function (result) {
// console.log(result);
// 1.解析并显示员工数据
build_emps_table(result);
// 2.解析并显示分页信息
bulid_page_info(result);
//解析显示分页条信息
build_page_nav(result);
//跳转页面
}
});
}
// 清空表单
function reset_form(ele) {
$(ele)[0].reset();
//清空样式
$(ele).removeClass("has-error has success");
$(ele).find(".help-block").text("");
}
// 绑定事件,点击新增按钮,弹出模态框
$("#emp_add_model_btn").click(function () {
reset_form("#empAddModel form")
// $("#empAddModel form")[0].reset();
// 发送ajax请求,查出部门信息,显示在下拉列表
getDepts("#empAddModel select");
$("#empAddModel").modal({
backdrop: "static"
})
});
//绑定编辑按钮,弹出模态框
$(document).on("click", ".edit_btn", function () {
getDepts("#empUpdateModel select");
//发送ajax请求,获取员工信息
var empId = $(this).attr("edit_id");
getEmp(empId);
// 把员工ID传递给更新按钮
$("#emp_update_btn").attr("edit_id", empId);
$("#empUpdateModel").modal({
backdrop: "static"
})
});
//绑定删除事件
$(document).on("click", ".delete_btn", function () {
var empName = $(this).parents("tr").find("td:eq(2)").text();
var empId = $(this).attr("delete_id");
if (confirm("确认删除【" + empName + "】吗")) {
//确认。发送ajax请求
$.ajax({
url: "${APP_PATH}/emps/" + empId,
type: "DELETE",
success: function (result) {
alert(result.msg);
//回到本页
to_page(currentPage);
}
})
}
})
//绑定保存按钮单击事件
$("#emp_save_btn").click(function () {
// 发送ajax请求前对提交的数据进行校验
if (validate_add_form() == false) {
return false;
}
//判断之前的ajax用户名校验是否成功
if ($(this).attr("ajax-va") == "error") {
return false;
}
$.ajax({
url: "${APP_PATH}/emps",
type: "POST",
data: $("#empAddModel form").serialize(),
success: function (result) {
if (result.code == 100) {
//员工保存成功
alert(result.msg);
//1.关闭模态框
$("#empAddModel").modal('hide');
//2.来到最后一页,显示刚才的数据(发送ajax请求)
to_page(totalRecord);
} else {
//显示错误信息
if (undefined != result.extend.errorFields.email) {
//显示邮箱错误信息
$("#email_add_input").parent().addClass("has-error");
$("#email_add_input").next("span").text(result.extend.errorFields.email);
}
if (undefined != result.extend.errorFields.empName) {
//显示用户名错误信息
$("#empName_add_input").parent().addClass("has-error");
$("#empName_add_input").next("span").text(result.extend.errorFields.empName);
}
}
}
});
});
//绑定批量删除事件
$("#emp_delete_all_btn").click(function () {
var empNames="";
var empIds="";
$.each($(".check_item:checked"), function () {
empNames += $(this).parents("tr").find("td:eq(2)").text() + ",";
empIds+=$(this).parents("tr").find("td:eq(1)").text()+"-";
})
empNames=empNames.substring(0,empNames.length-1);
empIds=empIds.substring(0,empIds.length-1);
if (confirm("你确定要删除【"+empNames+"】吗?")) {
$.ajax({
url:"${APP_PATH}/emps/"+empIds,
type:"DELETE",
success:function (result) {
alert(result.msg);
to_page(currentPage);
}
})
}
});
// 绑定更新按钮单击事件
$("#emp_update_btn").click(function () {
var email = $("#email_update_input").val();
var regEmail = /^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/
if (!regEmail.test(email)) {
$("#email_add_input").parent().addClass("has-error");
$("#email_add_input").next("span").text("邮箱格式不合法")
return false;
}
$("#email_add_input").parent().addClass("has-success");
$("#email_add_input").next("span").text("")
// 发送ajax请求进行保存
$.ajax({
url: "${APP_PATH}/emps/" + $(this).attr("edit_id"),
type: "PUT",
data: $("#empUpdateModel form").serialize(),
success: function () {
// alert(result.msg);
//关闭模态框
$("#empUpdateModel").modal("hide");
// 回到本页面
to_page(currentPage);
}
});
});
$("#empName_add_input").change(function () {
var empName = this.value;
//发送ajax请求至服务端校验是否可用
$.ajax({
url: "${APP_PATH}/checkuser",
data: "empName=" + empName,
type: "POST",
success: function (result) {
$("#empName_add_input").parent().removeClass("has-success has-error")
if (result.code == 100) {
$("#empName_add_input").parent().addClass("has-success");
$("#empName_add_input").next("span").text("用户名可用");
$("#emp_save_btn").attr("ajax-va", "success");
} else {
$("#empName_add_input").parent().addClass("has-error");
$("#empName_add_input").next("span").text(result.extend.va_msg);
$("#emp_save_btn").attr("ajax-va", "error");
}
}
})
});
//显示页面员工信息
function build_emps_table(result) {
//清空table表
$("#emps_table tbody").empty();
var emps = result.extend.pageInfo.list;
$.each(emps, function (index, item) {
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);
var editBtn = $("<button></button>").addClass("btn btn-info btn-sm edit_btn").append($("<span></span>").addClass("glyphicon glyphicon-pencil")).append("编辑");
editBtn.attr("edit_id", item.empId);
var delBtn = $("<button></button>").addClass("btn btn-danger btn-sm delete_btn").append($("<span></span>").addClass("glyphicon glyphicon-trash")).append("删除");
delBtn.attr("delete_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");
});
}
//全选,全不选
$("#check_all").click(function () {
// attr获取checked都是undefiened,使用prop修改和读取dom原生属性值
$(".check_item").prop("checked", $(this).prop("checked"));
})
//给每个单选框绑定单击事件
$(document).on("click", ".check_item", function () {
var flag = $(".check_item:checked").length == $(".check_item").length;
$("#check_all").prop("checked", flag);
});
// 解析显示分页信息
function bulid_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.total;
currentPage = result.extend.pageInfo.pageNum;
}
// 解析显示分页条
function build_page_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("«").attr("href", "#"));
if (result.extend.pageInfo.hasPreviousPage == false) {
firstPageLi.addClass("disabled");
prePageLi.addClass("disabled");
} else {
firstPageLi.click(function () {
to_page(1);
})
prePageLi.click(function () {
to_page(result.extend.pageInfo.pageNum - 1);
})
}
ul.append(firstPageLi).append(prePageLi);
$.each(result.extend.pageInfo.navigatepageNums, function (index, item) {
var numLi = $("<li></li>").append($("<a></a>").append(item).attr("href", "#"));
if (result.extend.pageInfo.pageNum == item) {
numLi.addClass("active");
}
numLi.click(function () {
to_page(item);
})
ul.append(numLi);
})
var nextPageLi = $("<li></li>").append($("<a></a>").append("»").attr("href", "#"));
var lastPageLi = $("<li></li>").append($("<a></a>").append("末页").attr("href", "#"));
if (result.extend.pageInfo.hasNextPage == false) {
nextPageLi.addClass("disabled");
lastPageLi.addClass("disabled");
} else {
lastPageLi.click(function () {
to_page(result.extend.pageInfo.pages);
})
nextPageLi.click(function () {
to_page(result.extend.pageInfo.pageNum + 1);
})
}
ul.append(nextPageLi).append(lastPageLi);
var navEle = $("<nav></nav>").append(ul);
$("#page_nav_area").append(navEle);
}
//查询部门信息函数
function getDepts(ele) {
$.ajax({
url: "${APP_PATH}/depts",
type: "GET",
success: function (result) {
// console.log(result);"extend":{"depts":[{"deptId":1,"deptName":"开发部"},{"deptId":2,"deptName":"测试部"}]
//显示下拉列表
$(ele).empty();
$.each(result.extend.depts, function () {
var optionEle = $("<option></option>").append(this.deptName).attr("value", this.deptId);
optionEle.appendTo(ele)
})
}
});
}
//查询员工信息函数
function getEmp(id) {
$.ajax({
url: "${APP_PATH}/emps/" + id,
type: "get",
success: function (result) {
// console.log(result);
// extend: {emp: {empId: 103, empName: "Tom", gender: "M", email: "Tom@qq.com", dId: 1, department: null}}
var empData = result.extend.emp;
$("#empName_upadate_static").text(empData.empName);
$("#email_update_input").val(empData.email);
$("#empUpdateModel input[type=radio]").val([empData.gender]);
$("#empUpdateModel select").val([empData.dId]);
}
});
}
//校验函数
function validate_add_form() {
//拿到校验的数据,使用正则表达式进行校验
var email = $("#email_add_input").val();
var regEmail = /^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/
if (!regEmail.test(email)) {
$("#email_add_input").parent().addClass("has-error");
$("#email_add_input").next("span").text("邮箱格式不合法")
return false;
} else {
$("#email_add_input").parent().addClass("has-success");
$("#email_add_input").next("span").text("")
}
return true;
}
</script>
</body>
</html>
项目构建中遇到的bug总结
无法加载Spring配置文件
java.lang.IllegalStateException: Failed to load ApplicationContext
并非在main目录下进行得测试,而是与main并列的Test文件目录下进行。
错误定义:
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
@RunWith(SpringJUnit4ClassRunner.class)
修改:在classpath后加上*:
@ContextConfiguration(locations = {"classpath*:applicationContext.xml"})
@RunWith(SpringJUnit4ClassRunner.class)
插入测试时候出现数据库连接错误
create connection SQLException, url: jdbc:mysql://localhost:3306/ssm, errorCode 1045, state 28000
错误原因: 配置文件中 username 与 Mysql 关键字冲突
修改dbdruid.properties中的username为user即可。
修改前:
使用MockMvc模拟请求时出错
具体异常:
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'employeeService': Unsatisfied dependency expressed through field 'employeeMapper'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'employeeMapper' defined in file [E:\workspace_idea1\SSMproject\ssm_crud\target\classes\com\js\dao\EmployeeMapper.class]: Unsatisfied dependency expressed through bean property 'sqlSessionFactory'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sqlSessionFactory' defined in URL [file:/E:/workspace_idea1/SSMproject/ssm_crud/target/classes/applicationContext.xml]: Invocation of init method failed; nested exception is java.io.FileNotFoundException: Could not open ServletContext resource [/mybatis-config]
末尾:Could not open ServletContext resource [/mybatis-config]
从最后可以看出是在spring中的mybatis配置无法读到。
检查spring中配置:
<!-- 与mybatis整合,可以生成sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="configLocation" value="mybatis-config"></property>
<property name="dataSource" ref="dataSource"></property>
修改为加上classpath:路径后,测试成功
<!-- 与mybatis整合,可以生成sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="configLocation" value="classpath:mybatis-config"></property>
<property name="dataSource" ref="dataSource"></property>