SSM整合案例
- 关于jdbcurl后面跟的参数问题
- SpringBoot中lombok提供的注解如下:
- 功能
- 技术
- 需要的依赖
- 通过cdn引入bootstrap及注意事项
- 使用MBG逆向工程
- 数据库环境搭建---含外键关联
- maven静态资源导出问题---在web.xml中进行配置
- 在spring核心配置容器中配置可以执行批量sqlsession
- Spring核心配置文件
- SpringMVC的核心配置文件
- Mybaits核心配置文件
-
- web.xml配置文件
- 使用分页插件
- controller层
-
- 查询所有员工并分页显示的方法
-
- 如何通过spring单元测试,完成对上面controller层代码的测试呢? 看下面的步骤
- 查询员工的思路---ajax
- 新增员工的思路
-
- ajax使用时需要注意的问题:当我们使用ajax向某个标签中追加数据的时候。例如点击一个按钮,向一个div追加5条记录,那么下一次点击按钮,又会调用一次ajax请求,向里面追击五条与先前重复的记录,因此每一次在追加之前,需要先将之前追加的记录或者追加的标签属性清除
- jquery中的正则表达式以及其他常用函数
- js清除表单内容的reset方法
- java中string类里面的matches校验正则表达式函数
- 使用springmvc的JSR303数据校验需要引入一下的依赖
- @Pattern注解中常用的校验正则表达式笔记
- jQuery之$(document)和on(events,[selector],[data],fn)方法
- jquery中的val可以获取或者设置对应的value值,设置单、多选框中被选中的状态
- springmvc两篇好文
- 占位符参数也会尝试去绑定函数中同名参数或者对象中同名属性
- ajax可以直接发送put和delete请求
- ajax发送put请求引发的血案
-
- 问题: 使用ajax直接发送put请求,封装的数据值为null
- 原因,这里也揭示了参数绑定的原理
- 血案:
-
- 注意如果不是直接使用ajax发送put请求,而是下面这种:
- 直接通过ajax发送put请求的解决方案:
- 删除员工
-
- jquery中获取所有祖先并可以加以筛选的函数
- 弹出框confirm()的使用
- ctrl+f快捷查找某个标签,变量在当前页面的具体位值
- length函数,返回查找到的元素个数
- 查找被选中的元素---checked
- prop函数,设置单选框是否被选中,使用true或者false
- jquery中的each方法来遍历数组和对象
- 在后代元素中进行筛选---find函数
- js中的substring和substr的区别
- 项目完整逻辑链代码
-
- 主页:这里是采用的模板,具体链接如下
- 员工展示页面,即增删查改页面
- POJO层除了逆向工程自动生成的四个类外,还额外增添了一个消息类msg,将其作为json字符串返回给前端,并且使用链式编程和静态函数,方便直接调用
- Dao层就是逆向工程自动生成的三个接口
-
- 注意:Dao层对应的三个逆向工程自动生成的.xml文件中,我们还需要增添两个查出员工的同时查出部门信息的方法,一个是单个员工,一个是多个员工,这里使用的是联合查询里面的左连接
- service层---两个类,一个处理部门的数据库操作,一个处理员工的数据库操作
- controller层对应也有两个类,一个处理部门请求,一个处理员工请求
- 总结
- ajax和js使用注意事项
关于jdbcurl后面跟的参数问题
解释jdbcUrl后面的参数useUnicode
Mysql jdbc URL连接参数useSSL、serverTimezone 相关问题
SpringBoot中lombok提供的注解如下:
@Data注解如何使用
java添加@Data注解的步骤
@Data注解使用/注解getset不起作用
功能
技术
需要的依赖
<!--导入spring的坐标-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<!--导入aspectj的坐标-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.4</version>
</dependency>
<!--引入Spring测试坐标-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<!--junit坐标-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
</dependency>
<!--spring tx的坐标,处理事务的-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<!--spring jdbc的坐标-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<!--SpringWeb模块相关的坐标-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<!--jstl的依赖-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!--标准标签库的依赖-->
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
<!-- maven管理引入springmvc注解数据校验所需jar包:-->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.1.0.Final</version>
</dependency>
<!--文件上传的依赖-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.5</version>
</dependency>
<!--引入jquery的依赖-->
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.3.1-2</version>
</dependency>
<!--jackSon的依赖===>ajax-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.10.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.0</version>
</dependency>
<!--MyBaits依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.1</version>
</dependency>
<!--log4j日志依赖-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.14.1</version>
</dependency>
<!--ehcache第三方专业缓存框架的依赖-->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-core</artifactId>
<version>2.6.8</version>
</dependency>
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.0.3</version>
</dependency>
<!--除了log4j,还需要导入slf4j-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.21</version>
<scope>test</scope>
</dependency>
<!--mysql驱动的坐标-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.32</version>
</dependency>
<!--c3p0数据库连接池的坐标-->
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<!--druid数据库连接池坐标-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<!--mybaits和spring整合包的依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.2</version>
</dependency>
<!--lombok注解-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.10</version>
</dependency>
<!--jsp相关依赖-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
<scope>provided</scope>
</dependency>
<!--pagehelper-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.0.3</version>
</dependency>
<!-- pagehelper的依赖包:jsqlparser -->
<dependency>
<groupId>com.github.jsqlparser</groupId>
<artifactId>jsqlparser</artifactId>
<version>0.9.5</version>
</dependency>
<!--mybatis的逆向工程-->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.7</version>
</dependency>
<!--使用插件启动mbg-->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.7</version>
</dependency>
<!-- maven管理引入springmvc注解数据校验所需jar包:-->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.1.0.Final</version>
</dependency>
通过cdn引入bootstrap及注意事项
<link href="https://cdn.bootcss.com/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.bootcss.com/twitter-bootstrap/3.3.7/css/bootstrap-theme.min.css" rel="stylesheet">
<script src="http://cdn.bootcss.com/jquery/1.11.1/jquery.min.js"></script>
<script src="http://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
引用时的注意事项
使用MBG逆向工程
MBG逆向工程使用说明
数据库环境搭建—含外键关联
maven静态资源导出问题—在web.xml中进行配置
<!--让IDEA不要忽略src目录下的xml文件-->
<!--静态资源导出问题-->
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
在spring核心配置容器中配置可以执行批量sqlsession
<!--配置可以执行批量sqlsession-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactoryBean"></constructor-arg>
<constructor-arg name="executorType" value="BATCH"></constructor-arg>
</bean>
/*使用spring的单元测试*/
//指定spring配置文件的位置
@ContextConfiguration(locations = {"classpath:Spring/applicationContext.xml"})
//运行单元测试使用spring环境
@RunWith(SpringJUnit4ClassRunner.class)
public class TestDao {
//直接注入我们需要使用的组件
@Autowired
departmentMapper departmentMapper;
@Autowired
employeeMapper eMapper;
//批量的sqlsession
@Autowired
SqlSession sqlSession;
@Test
public void testDao()
{
//生成员工数据,插入员工
eMapper.insertSelective(new employee(null,"王五","男","3076679687@qq.com",4));
employeeMapper mapper=sqlSession.getMapper(employeeMapper.class);
for(int i=0;i<50;i++)
{
//每一次生成不重复的随机字符串,截取前五个字符
String uid = UUID.randomUUID().toString().substring(0, 5);
mapper.insertSelective(new employee(null,uid,"男","@307667968"+i+"@qq.com",1));
}
}
}
Spring核心配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mybatis="http://mybatis.org/schema/mybatis-spring"
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
http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring.xsd">
<!--Spring配置文件,这里注意配置和业务逻辑有关的-->
<!--spring除了控制器不要,剩下的业务逻辑组件都要,包括dao,包括service-->
<context:component-scan base-package="Com">
<!--扫描排除不写use,使用默认扫描规则-->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--加载外部的properties文件-->
<!--当前要加载的properties文件在资源文件下,前面需要加上classpath:-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--
c3p0:自动化操作(自动化加载配置文件,并且可以自动设置到对象中)
-->
<!--配置数据源-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!--通过键值的方式引入值-->
<property name="driverClass" value="${jdbc.driver}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property>
<property name="minPoolSize" value="${jdbc.minPoolSize}"></property>
<!--关闭连接后不自动commit-->
<property name="autoCommitOnClose" value="false"/>
<!--获取连接超时时间-->
<property name="checkoutTimeout" value="10000"/>
<!--获取连接失败重试的次数-->
<property name="acquireRetryAttempts" value="2"/>
</bean>
<!--配置使用Mybaits操作数据库-->
<!--可以根据配置文件得到SqlSessionFactory-->
<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--指定mybaits全局配置文件的位置·-->
<property name="configLocation" value="classpath:MyBaits/mybaits-config.xml"></property>
<property name="dataSource" ref="dataSource"></property>
<!-- 指定xml映射文件位置--> <!--所有xml都是我们的映射文件-->
<property name="mapperLocations" value="classpath:MyBaits/mapper/*.xml"></property>
</bean>
<!--配置扫描器: 我们要把dao接口的实现加入到ioc容器-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--扫描所有dao接口的实现,加入到IOC容器中-->
<property name="basePackage" value="Com.Dao"></property>
<!-- 因为会自动装配 SqlSessionFactory和SqlSessionTemplate
所以没 有 必 要 去 指 定 SqlSessionFactory或 SqlSessionTemplate
因此可省略不配置;
但是,如果你使 用了一个 以上的 DataSource,那么自动装配可能会失效。
这种 情况下,你可以使用sqlSessionFactoryBeanName或sqlSessionTemplateBeanName属性来设置正确的 bean名称来使用;-->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean"/>
</bean>
<!--配置可以执行批量sqlsession-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactoryBean"></constructor-arg>
<constructor-arg name="executorType" value="BATCH"></constructor-arg>
</bean>
<!--配置事务控制,配置事务管理器,控制住数据源里面的链接的关闭和提交-->
<!--创建事务管理器对象-->
<!--需要导入aspectj的坐标,即面向切面编程的坐标-->
<!--这里的id可以随便写-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--操作事务需要连接对象,连接对象在连接池中(数据源)-->
<!--控制数据源,通过操作connection连接,来进行事务的回滚,自动提交操作-->
<property name="dataSource" ref="dataSource"/>
</bean>
<!--基于xml配置事务,哪些方法切入事务还需要写切入点表达式-->
<aop:config>
<!--配置切入点表达式-->
<aop:pointcut id="tx" expression="execution(* Com.service.*.*(..))"/>
<aop:advisor advice-ref="myTx" pointcut-ref="tx"/>
</aop:config>
<!--配置事务增强,事务如何切入-->
<tx:advice id="myTx" transaction-manager="transactionManager"><!--这里不写transaction-manager
,那么默认会去找id为transactionManager的-->
<tx:attributes>
<!--所有方法都是事务方法,任何事务方法出现任何异常都回滚-->
<tx:method name="*" rollback-for="java.lang.Exception"/>
<!--以get开始的所有方法-->
<tx:method name="get*" read-only="true"/>
</tx:attributes>
</tx:advice>
</beans>
SpringMVC的核心配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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">
<!--SpringMVC配置文件,包含网站跳转逻辑的控制和配置-->
<!--SpringMVC只扫描控制器:禁用默认过滤规则-->
<context:component-scan base-package="Com" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<!--文件上传解析器: id必须是multipartFile-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--设置文件上传最大量为5m-->
<property name="maxUploadSize" value="5242880"></property><!--spel运算符,计算最大可上传文件体积-->
<!--设置默认的编码-->
<property name="defaultEncoding" value="utf-8"></property>
</bean>
<!--扫描静态资源-->
<mvc:default-servlet-handler/>
<!--扫描动态资源,也支持一些高级功能:JSR303校验,快捷的ajax请求-->
<mvc:annotation-driven/>
</beans>
Mybaits核心配置文件
<?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">
<configuration>
<settings>
<!--开启驼峰命名规则-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- 打印sql日志 -->
<setting name="logImpl" value="STDOUT_LOGGING" />
<!--开启延迟加载开关-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--开启属性按需加载-->
<setting name="aggressiveLazyLoading" value="false"/>
<!--开启全局缓存开关-->
<setting name="cacheEnabled" value="true"/>
</settings>
<!-- 给自定义POJO包下的所有类起别名,默认都是类名-->
<typeAliases>
<package name="Com.POJO"/>
</typeAliases>
<!--配置pageHelper-->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!--分页参数合理化,当小于0时,查询第一页,当大于总页数时,查询最后一页-->
<property name="reasonable" value="true"/>
</plugin>
</plugins>
</configuration>
web.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<!--配置Spring的ioc容器启动-->
<context-param>
<param-name>contextConfigLocation</param-name>
<!--指定spring配置文件位置-->
<param-value>classpath:Spring/applicationContext.xml</param-value>
</context-param>
<!--配置监听器-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--配置springmvc的前端控制器-->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<!--这里我们spring和springmvc分离开了,这里相当于配置springmvc的ioc容器启动-->
<param-value>classpath:SpringMVC/springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--两个标准配置-->
<!--字符编码-->
<!--配置一个字符编码的Filter,放在所有过滤器之前-->
<filter>
<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>
<!--支持REST风格的filter,将put或者delete请求转换为put请求-->
<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>
<!--spring提供的过滤器解决解决ajax直接发送put请求-->
<filter>
<filter-name>HttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HttpPutFormContentFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--设置session过期时间-->
<session-config>
<session-timeout>15</session-timeout>
</session-config>
</web-app>
使用分页插件
pageHelper----Mybaits分页插件
controller层
查询所有员工并分页显示的方法
//查询所有员工,分页展示
@RequestMapping("/emps")
public String getEmps(@RequestParam(value = "pn",defaultValue = "1") Integer pn, Model model)
{
//传入当前显示的页码和每页显示记录的条数,查询语句紧跟在后面
PageHelper.startPage(pn,5);
List<employee> emps = es.getAll();
//使用pageINfo包装查询后的结果
//封装了详细的分页信息,包括查询出来的数据,传入连续显示的页数
PageInfo page=new PageInfo(emps,5);
model.addAttribute("info",page);
return "list";
}
如何通过spring单元测试,完成对上面controller层代码的测试呢? 看下面的步骤
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration//加了这个注解后,才能获取到springmvc的ioc容器
@ContextConfiguration(locations = {"classpath:Spring/applicationContext.xml","classpath:SpringMVC/springmvc.xml"})
public class SpringMVCTers {
//传入springmvc的ioc
@Autowired
WebApplicationContext context;
//虚拟mvc请求,获取到处理结果
MockMvc mockMvc;
@Before
public void initMokcMvc()
{
mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
}
@Test
public void testPage() throws Exception {
//模拟请求,拿到返回值
MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get("/emps").param("pn", "1")).andReturn();
//请求成功以后,请求域中会有info,我们可以取出info进行验证
MockHttpServletRequest request=result.getRequest();
PageInfo pi= (PageInfo) request.getAttribute("info");
System.out.println("当前页码:"+pi.getPageNum());
System.out.println("总页码:"+pi.getPages());
System.out.println("总记录数:"+pi.getTotal());
System.out.println("在页面需要连续显示的页码:");
int[] nums = pi.getNavigatepageNums();
for(int i:nums)
{
System.out.println(" "+i);
}
//获取员工数据
List<employee> list = pi.getList();
for(employee e:list)
System.out.println("姓名:"+e.getEmpName()+" "+"性别: "+e.getGender());
}
}
查询员工的思路—ajax
员工展示页面:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%pageContext.setAttribute("ctx",request.getContextPath());%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<link href="https://cdn.bootcss.com/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.bootcss.com/twitter-bootstrap/3.3.7/css/bootstrap-theme.min.css" rel="stylesheet">
<script src="http://cdn.bootcss.com/jquery/1.11.1/jquery.min.js"></script>
<script src="http://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<html>
<head>
<title>员工信息展示页面</title>
</head>
<body>
<%--搭建展示页面--%>
<div class="container">
<%--标题--%>
<div class="row">
<div class="col-md-12">
<h1 style="font-style: italic" >大忽悠集团员工展示页面</h1>
</div>
</div>
<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 class="row">
<div class="col-md-12">
<table class="table table-hover" id="emps_table">
<thead>
<tr class="info">
<th>#</th>
<th>姓名</th>
<th>性别</th>
<th>邮箱</th>
<th>部门名字</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<%--使用ajax向里面添加内容--%>
</tbody>
</table>
</div>
</div>
</div>
<%--显示分页信息--%>
<div class="row">
<%--分页文字信息--%>
<div class="col-md-6" id="page_info-area">
<%--使用ajax显示相关信息--%>
</div>
<%--分页条信息--%>
<div class="col-md-6" id="page_info_nav">
<%--使用ajax显示相关信息--%>
</div>
</div>
</div>
<script>
//1.页面加载完成以后,直接发送ajax请求,要到分页数据
$(function (){
//一开始去首页
toPage(1)
})
//跳转到指定页码号
function toPage(pn){
$.ajax({
url:"${ctx}/emps",
data:"pn="+pn,
type:"get",
success:function (result)
{
//1。解析并显示员工数据
build_emps_table(result);
//2。解析并显示分页信息
build_page_info(result);
//3.解析显示分页条数据
build_page_nav(result);
},
dataType:"json"
})
}
function build_emps_table(result){
//每次调用该方法前,需要先请客表格,因为ajax是无刷更新,每一次调用都会重复追加
$("#emps_table tbody").empty();
//拿到员工数组
var e=result.extent.info.list;
$.each(e,
function(index,item)
{
//动态创建标签并向里面添加内容
var empID=$("<td></td>").append(item.empId);
var empName=$("<td></td>").append(item.empName);
var empGender=$("<td></td>").append(item.gender);
var empEmail=$("<td></td>").append(item.email);
var deptName=$("<td></td>").append(item.department.depName);
//构建两个按钮 addClass方法调用后,返回原来的元素
var editBtn=$("<button></buttom>").addClass("btn btn-primary btn-sm")
.append("<span></span>").addClass("glyphicon glyphicon-pencil").append("编辑");
var delBtn=$("<button></buttom>").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(empID).append(empName).append(empGender)
.append(empEmail).append(deptName).append(btnTd).
appendTo("#emps_table tbody");//整个表格构建完成以后,添加到对应位置中
}
)
}
//解析显示分页信息
function build_page_info(result)
{
//每次调用前,先清空所有之前添加的内容
$("#page_info-area").empty();
$("#page_info-area").append("当前第"+result.extent.info.pageNum+"页,总"+result.extent.info.pages+"共页,总共" +
result.extent.info.total + "条记录");
}
//解析显示分页条,点击分页要能去下一页
function build_page_nav(result) {
//每次调用前,先清空所有之前添加的内容
$("#page_info_nav").empty();
var ul = $("<ul></ul>").addClass("pagination");
//attr赋值后,还是会返回当前元素,这里不能点击跳转,所以传入#,跳转到本页
var firstPageLi = $("<li></li>").append($("<a></a>").append("首页").attr("href", "#"));
var prePageLi = $("<li></li>").append($("<a></a>").append("«"));
//如果是首页,那么前进一页不能点击
if (result.extent.info.hasPreviousPage == false) {
firstPageLi.addClass("disabled");
prePageLi.addClass("disabled");
} else
{
firstPageLi.click(function (){
toPage(1);
});
prePageLi.click(function (){
toPage(result.extent.info.pageNum+1);
});
}
var nextPageLi=$("<li></li>").append($("<a></a>").append("»"));
var lastPageLi=$("<li></li>").append($("<a></a>").append("末页").attr("href","#"));
//如果是末页,没有下一页对应也不能点
if(result.extent.info.hasNextPage==false)
{
nextPageLi.addClass("disabled");
lastPageLi.addClass("disabled");
}else
{
lastPageLi.click(function (){
toPage(result.extent.info.pages);
});
nextPageLi.click(function (){
toPage(result.extent.info.pageNum+1);
});
}
//添加首页和前一页的提示
ul.append(firstPageLi).append(prePageLi);
//遍历页码号
$.each(result.extent.info.navigatepageNums,function (index,item){
var numLi=$("<li></li>").append($("<a></a>").append(item));
if(result.extent.info.pageNum==item)
{
//如果是当前页,那么对于页码处于激活状态
numLi.addClass("active");
}
numLi.click(function (){
toPage(item);
});
ul.append(numLi);
});
//添加下一页和末尾的提示
ul.append(nextPageLi).append(lastPageLi);
//把ul加入到nav中
var navEle=$("<nav></nav>").attr("aria-label","Page navigation").append(ul);
navEle.appendTo("#page_info_nav");
}
</script>
</body>
</html>
目前controller层:
@Controller
public class EmployeeController {
@Autowired
EmployueeService es;
//跳转到首页
@RequestMapping("/emp")
public String getEmps()
{
return "list";
}
@RequestMapping("/tolist")
public String toList(user u)
{
System.out.println("tolist");
user admin = es.getUser();
if(u.getName().equals(admin.getName())&&u.getPassword().equals(admin.getPassword()))
return "redirect:/emp";
return "redirect:/index.jsp";
}
//使用ajax技术,显示信息
@RequestMapping("/emps")
@ResponseBody//这里需要导入json的依赖
public msg getEmpWithPageinfo(@RequestParam(value = "pn",defaultValue = "1") Integer pn, Model model)
{
//传入当前显示的页码和每页显示记录的条数,查询语句紧跟在后面
PageHelper.startPage(pn,5);
List<employee> emps = es.getAll();
//使用pageINfo包装查询后的结果
//封装了详细的分页信息,包括查询出来的数据,传入连续显示的页数
PageInfo page=new PageInfo(emps,5);
model.addAttribute("info",page);
System.out.println("emps");
//以json形式返回---包含info信息和失败成功信息
return msg.success().add("info",page);
}
}
目前效果:
新增员工的思路
ajax使用时需要注意的问题:当我们使用ajax向某个标签中追加数据的时候。例如点击一个按钮,向一个div追加5条记录,那么下一次点击按钮,又会调用一次ajax请求,向里面追击五条与先前重复的记录,因此每一次在追加之前,需要先将之前追加的记录或者追加的标签属性清除
jquery中的正则表达式以及其他常用函数
jQuery中的正则表达式
Jquery中的正则表达式注意事项
jquery中append()和appendTo()的区别
jQuery 文档操作 - empty() 方法
jQuery中empty和remove方法
jquery的each遍历,this指向
jQuery的change()事件
jquery attr和data给元素添加自定义属性
jQuery中.find()方法?
js清除表单内容的reset方法
使用jquery获取到要重置的表单后,需要取出数组中的dom表单对象
//清除表单数据(表单重置)---DOM里面的方法,而不是jquery里面的方法
$("#myModal form")[0].reset();
java中string类里面的matches校验正则表达式函数
Java字符串:matches() 方法
使用springmvc的JSR303数据校验需要引入一下的依赖
低版本tomcat需要引入validation-api,高版本不需要
<!-- maven管理引入springmvc注解数据校验所需jar包:-->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.1.0.Final</version>
</dependency>
@Pattern注解中常用的校验正则表达式笔记
@Pattern注解中常用的校验正则表达式笔记
@Pattern进行正则校验
jQuery之$(document)和on(events,[selector],[data],fn)方法
jQuery之$(document)
jquery中的val可以获取或者设置对应的value值,设置单、多选框中被选中的状态
springmvc两篇好文
SpringMVC学习01:请求路径匹配和参数绑定
SpringMVC01----请求匹配、参数绑定、常用注解
占位符参数也会尝试去绑定函数中同名参数或者对象中同名属性
ajax可以直接发送put和delete请求
$.ajax({
//这里把员工id传递到更新按钮上,这样这里的id参数就可以直接从按钮上获得
url:"${ctx}/updateEmp/"+$(this).attr("edit-id"),
type:"put",
data:$("#updateModel form").serialize(),//表单序列化,这里是rest风格的url,因此请求参数中要附带一个_method参数
success:function (res){
alert("成功")
alert(res.msg);
}
ajax发送put请求引发的血案
//员工更新方法
@RequestMapping(value = "/updateEmp/{empId}",method = RequestMethod.PUT)
@ResponseBody
public msg updateEmp(employee e)
{
System.out.println("=======================================");
System.out.println("员工更新方法");
System.out.println(e);
System.out.println("=======================================");
System.out.println(e);
es.updateEmp(e);
return msg.success();
}
问题: 使用ajax直接发送put请求,封装的数据值为null
原因,这里也揭示了参数绑定的原理
getParamter()里面传入的参数是POJO对象每个属性的名字,他会尝试把每一个属性的名字作为key去请求参数中进行匹配获取
血案:
注意如果不是直接使用ajax发送put请求,而是下面这种:
直接通过ajax发送put请求的解决方案:
解决方案:在web.xml中配置spring提供的过滤器解决
<filter>
<filter-name>HttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HttpPutFormContentFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
删除员工
jquery中获取所有祖先并可以加以筛选的函数
弹出框confirm()的使用
弹出框confirm()的使用
ctrl+f快捷查找某个标签,变量在当前页面的具体位值
length函数,返回查找到的元素个数
查找被选中的元素—checked
prop函数,设置单选框是否被选中,使用true或者false
jquery中的each方法来遍历数组和对象
在后代元素中进行筛选—find函数
js中的substring和substr的区别
js中substring()与substr()方法的区别
项目完整逻辑链代码
主页:这里是采用的模板,具体链接如下
登录模板
员工展示页面,即增删查改页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%pageContext.setAttribute("ctx",request.getContextPath());%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<link href="https://cdn.bootcss.com/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.bootcss.com/twitter-bootstrap/3.3.7/css/bootstrap-theme.min.css" rel="stylesheet">
<script src="http://cdn.bootcss.com/jquery/1.11.1/jquery.min.js"></script>
<script src="http://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<html>
<head>
<title>员工信息展示页面</title>
</head>
<body>
<!-- 修改员工的模态框---bootstrap -->
<div class="modal fade" id="updateModel" 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="myModalUpdate">修改员工信息</h4>
</div>
<div class="modal-body">
<%--表单添加在此处--%>
<form class="form-horizontal" method="post">
<div class="form-group">
<label for="inputName" class="col-sm-2 control-label">姓名:</label>
<div class="col-sm-10">
<p class="form-control-static" id="UpdateEmpName"></p>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">性别:</label>
<div class="col-sm-10">
<label class="radio-inline">
<input type="radio" name="gender" value="男" checked="checked"> 男
</label>
<label class="radio-inline">
<input type="radio" name="gender" value="女" > 女
</label>
</div>
</div>
<div class="form-group">
<label for="inputEmail3" class="col-sm-2 control-label" >邮箱</label>
<div class="col-sm-10">
<input type="email" class="form-control" id="updateEmail3" name="email" placeholder="请输入邮箱">
<span class="help-block"></span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">部门</label>
<div class="col-sm-4">
<select class="form-control" name="dpId" >
<%--下拉列表的内容是动态从数据库中取出的,并且部门提交部门id即可--%>
<%--通过ajax显示所有员工信息--%>
</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="update_btn">更新</button>
</div>
</div>
</div>
</div>
<!-- 新增员工的模态框---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">添加新员工</h4>
</div>
<div class="modal-body">
<%--表单添加在此处--%>
<form class="form-horizontal" method="post">
<div class="form-group">
<label for="inputName" class="col-sm-2 control-label">姓名:</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="inputName" name="empName" placeholder="请输入员工姓名" >
<span class="help-block"></span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">性别:</label>
<div class="col-sm-10">
<label class="radio-inline">
<input type="radio" name="gender" value="男" checked="checked"> 男
</label>
<label class="radio-inline">
<input type="radio" name="gender" value="女" > 女
</label>
</div>
</div>
<div class="form-group">
<label for="inputEmail3" class="col-sm-2 control-label" >邮箱</label>
<div class="col-sm-10">
<input type="email" class="form-control" id="inputEmail3" name="email" placeholder="请输入邮箱">
<span class="help-block"></span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">部门</label>
<div class="col-sm-4">
<select class="form-control" name="dpId" >
<%--下拉列表的内容是动态从数据库中取出的,并且部门提交部门id即可--%>
<%--通过ajax显示所有员工信息--%>
</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">保存</button>
</div>
</div>
</div>
</div>
<%--搭建展示页面--%>
<div class="container">
<%--标题--%>
<div class="row">
<div class="col-md-12">
<h1 style="font-style: italic" >大忽悠集团员工展示页面</h1>
</div>
</div>
<div class="row">
<%--按钮--%>
<div class="col-md-4 col-md-offset-8">
<button class="btn btn-primary" id="add_btn">新增</button>
<button class="btn btn-danger" id="del_btn">删除</button>
</div>
<%--显示表格数据--%>
<div class="row">
<div class="col-md-12">
<table class="table table-hover" id="emps_table">
<thead>
<tr class="info">
<th>
<input type="checkbox" id="check-all"/>
</th>
<th>#</th>
<th>姓名</th>
<th>性别</th>
<th>邮箱</th>
<th>部门名字</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<%--使用ajax向里面添加内容--%>
</tbody>
</table>
</div>
</div>
</div>
<%--显示分页信息--%>
<div class="row">
<%--分页文字信息--%>
<div class="col-md-6" id="page_info-area">
<%--使用ajax显示相关信息--%>
</div>
<%--分页条信息--%>
<div class="col-md-6" id="page_info_nav">
<%--使用ajax显示相关信息--%>
</div>
</div>
</div>
<script>
//1.页面加载完成以后,直接发送ajax请求,要到分页数据
$(function (){
//一开始去首页
toPage(1)
})
//用一个全局变量,保存总的记录数
var totalNum;
//定义一个全局变量,保存当前页码
var curNum;
//跳转到指定页码号
function toPage(pn){
$.ajax({
url:"${ctx}/emps",
data:"pn="+pn,
type:"get",
success:function (result)
{
//1。解析并显示员工数据
build_emps_table(result);
//2。解析并显示分页信息
build_page_info(result);
//3.解析显示分页条数据
build_page_nav(result);
},
dataType:"json"
})
}
function build_emps_table(result){
//每次调用该方法前,需要先请客表格,因为ajax是无刷更新,每一次调用都会重复追加
$("#emps_table tbody").empty();
//拿到员工数组
var e=result.extent.info.list;
$.each(e,
function(index,item)
{
//还需要追加一个多选框用于批量删除
var checkBoxId=$("<td><input type='checkbox' class='check-item'/></td>");
//动态创建标签并向里面添加内容
var empID=$("<td></td>").append(item.empId);
var empName=$("<td></td>").append(item.empName);
var empGender=$("<td></td>").append(item.gender);
var empEmail=$("<td></td>").append(item.email);
var deptName=$("<td></td>").append(item.department.depName);
//构建两个按钮 addClass方法调用后,返回原来的元素
var editBtn=$("<button></buttom>").addClass("btn btn-primary btn-sm edit_btn")//添加一个edit-btn属性方便操作
.append("<span></span>").addClass("glyphicon glyphicon-pencil").append("编辑");
//为编辑按钮添加一个自定义的属性,来表示当前员工id
editBtn.attr("edit-id",item.empId);
var delBtn=$("<button></buttom>").addClass("btn btn-danger btn-sm del_btn")
.append("<span></span>").addClass("glyphicon glyphicon-trash").append("删除");
//为删除按钮添加一个自定义的属性,来表示当前员工的id
delBtn.attr("del-id",item.empId);
//将两个按钮追加到一个单元格里面
var btnTd=$("<td></td>").append(editBtn).append(" ").append(delBtn);
//append方法执行完成之后还是返回原来的元素
$("<tr></tr>").append(checkBoxId).append(empID).append(empName).append(empGender)
.append(empEmail).append(deptName).append(btnTd).
appendTo("#emps_table tbody");//整个表格构建完成以后,添加到对应位置中
}
)
}
//解析显示分页信息
function build_page_info(result)
{
//每次调用前,先清空所有之前添加的内容
$("#page_info-area").empty();
$("#page_info-area").append("当前第"+result.extent.info.pageNum+"页,总"+result.extent.info.pages+"共页,总共" +
result.extent.info.total + "条记录");
totalNum=result.extent.info.total;
curNum=result.extent.info.pageNum;
}
//解析显示分页条,点击分页要能去下一页
function build_page_nav(result) {
//每次调用前,先清空所有之前添加的内容
$("#page_info_nav").empty();
var ul = $("<ul></ul>").addClass("pagination");
//attr赋值后,还是会返回当前元素,这里不能点击跳转,所以传入#,跳转到本页
var firstPageLi = $("<li></li>").append($("<a></a>").append("首页").attr("href", "#"));
var prePageLi = $("<li></li>").append($("<a></a>").append("«"));
//如果是首页,那么前进一页不能点击
if (result.extent.info.hasPreviousPage == false) {
firstPageLi.addClass("disabled");
prePageLi.addClass("disabled");
} else
{
firstPageLi.click(function (){
toPage(1);
});
prePageLi.click(function (){
toPage(result.extent.info.pageNum+1);
});
}
var nextPageLi=$("<li></li>").append($("<a></a>").append("»"));
var lastPageLi=$("<li></li>").append($("<a></a>").append("末页").attr("href","#"));
//如果是末页,没有下一页对应也不能点
if(result.extent.info.hasNextPage==false)
{
nextPageLi.addClass("disabled");
lastPageLi.addClass("disabled");
}else
{
lastPageLi.click(function (){
toPage(result.extent.info.pages);
});
nextPageLi.click(function (){
toPage(result.extent.info.pageNum+1);
});
}
//添加首页和前一页的提示
ul.append(firstPageLi).append(prePageLi);
//遍历页码号
$.each(result.extent.info.navigatepageNums,function (index,item){
var numLi=$("<li></li>").append($("<a></a>").append(item));
if(result.extent.info.pageNum==item)
{
//如果是当前页,那么对于页码处于激活状态
numLi.addClass("active");
}
numLi.click(function (){
toPage(item);
});
ul.append(numLi);
});
//添加下一页和末尾的提示
ul.append(nextPageLi).append(lastPageLi);
//把ul加入到nav中
var navEle=$("<nav></nav>").attr("aria-label","Page navigation").append(ul);
navEle.appendTo("#page_info_nav");
}
//重置表单的函数
function reset_form(ele)
{
//清空表单里面的内容体
$(ele)[0].reset();
//清空之前给可能会给表单添加的样式
//清除表单下面所有后代中含有这两个属性的
$(ele).find("*").removeClass("has-error has-success");
$(ele).find(".help-block").text("");
};
//按钮被点击,显示模态框
$("#add_btn").click(function (){
//清除表单数据(表单重置)---DOM里面的方法,而不是jquery里面的方法
//这里的表单重置,不应该只是重置表单里面的内容体,包括给表单添加的样式
reset_form("#myModal form");
//发送ajax请求,查出部门信息,显示在下拉列表中
getDepts("#myModal select");
$('#myModal').modal({
backdrop:false //点击页面其他地方,模态框也不会关闭,或者这里可以写static
});
})
//查出所有部门信息,并显示在下拉列表中
function getDepts(ele)
{
//每次调用该方法前,先将之前添加在下拉列表中的数据清除---ajax使用的主要问题
$(ele).empty();
$.ajax({
url:"${ctx}/depts",
type: "GET",
success:function (result){
//显示部门信息在下拉列表中
$.each(result.extent.depts,function (){
//this指代当前遍历的元素
var optionEle=$("<option></option>").append(this.depName).attr("value",this.depId);
//模态框下面只有一个select标签
optionEle.appendTo(ele);
})
}
})
}
//校验表单数据
function valid_add_form()
{
//1.拿到要检验的数据,使用正则表达式
var empName=$("#inputName").val();
//正则表达式
var regName=/(^[a-zA-Z0-9_-]{6,16}$)|(^[\u2E80-\u9FFF]{2,5}$)/
//每一次给当前标签添加样式前,应该清除掉之前添加的样式
if(!regName.test(empName))
{
//调用抽取出来的显示校验结果的方法
show_valid_msg("#inputName","error","用户名可以是2-5位中文或者6-16位英文和数字的组合");
return false;
}else
{
show_valid_msg("#inputName","success","");
}
//校验邮箱
var email=$("#inputEmail3").val();
var regEmail=/^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/;
if(!regEmail.test(email))
{
show_valid_msg("#inputEmail3","error","邮箱格式有误");
return false;
}
else
{
show_valid_msg("#inputEmail3","success","");
}
return true;
}
//抽取出展示校验结果信息的方法
function show_valid_msg(ele,status,msg)
{
//清除当前元素的校验状态---移除多个属性,用空格分隔开来
$(ele).parent().removeClass("has-success has-error")
$(ele).next("span").text("");
if(status=="success")
{
$(ele).parent().addClass("has-success");
$(ele).next("span").text(msg);
}
if(status=="error")
{
$(ele).parent().addClass("has-error");
$(ele).next("span").text(msg);
}
}
//将用户名提交到后端,验证是否可用---用户名是否重复---前端校验
$("#inputName").change(function (){
//获取输入框的value值
var empName=this.value;
//发送ajax请求校验用户名是否可用
$.ajax({
url:"${ctx}/checkEmp",
data:"empName="+empName,
type:"post",
success:function (result){
if(result.code==100)
{
show_valid_msg("#inputName","success","用户名可用");
//如果校验成功了,给保存按钮上增加一个自定义属性
$("#emp_save").attr("ajax-va","success");
}else
{
//这里如果错误了,那么动态显示是用户名重复错误,还是校验错误
show_valid_msg("#inputName","error",result.extent.va_msg);
$("#emp_save").attr("ajax-va","error");
}
}
});
});
//当保存按钮点击以后,提交表单的数据
$("#emp_save").click(function (){
//1.模态框中填写的表单数据提交给服务器进行保存
//先对要提交给服务器的数据进行数据校验
if(!valid_add_form())
return false;
//下面是前端校验用户名是否重复
//1.判断之前的用户名校验是否成功,获取到当前按钮的校验状态,上面设置的自定义校验属性
//这里还有一个问题需要处理,那就是当我们第一次添加一名符合规定的员工后,下一次打开表单还是上一次符合规定的员工
//并且如果我们不对数据进行修改,那么它的用户名校验状态就是合法的,那么直接再次提交,也不会发送ajax请求进行用户名校验
//这样就会造成人员重复添加的问题,因此这里每一次点击新增按钮,弹出模态对话框的时候,对表单进行清除操作
if($(this).attr("ajax-va")=="error")
return false;
//2.发送ajax请求保存员工
$.ajax({
urL:"${ctx}/emp",
type:"post",
data:$("#myModal form").serialize(),//序列化表格内容为字符串。
success:function (result){
//员工保存成功:
//关闭模态框之前查看后端校验结果
if(result.code==100)
{
//1.关闭模态框
$("#myModal").modal("hide");
//2.来到最后一页,显示添加的这条记录
//发送ajax请求,显示最后一页数据
//将总记录数当做页码,分页插件如果页码大于总页码,显示最后一页,直接在xml中配置过了
toPage(totalNum);
}
else
{
//后端校验用户名是否重复
if(undefined!=result.extent.errorName)
{
alert("用户名重复");
show_valid_msg("#inputName","error",result.extent.errorName);
}
//这里可以将前端校验的方法注释掉,检验后端检验正确与否
//有哪个字段有错误,就显示哪个字段的错误信息
//如果这里某个字段没有错误信息,那么会显示undefined
if(undefined!=result.extent.errorFields.email)
{
alert("1");
//显示邮箱错误信息
show_valid_msg("#inputEmail3","error","后端检验发现邮箱格式有误");
}
if(undefined!=result.extent.errorFields.empName)
{
alert("2");
//显示名字错误信息
show_valid_msg("#inputName","error","后端检验发现用户名格式有误");
}
}
}
});
});
//此处不能直接绑定click的原因是因为,发送ajax请求动态往标签体内增加内容是在页面加载完成以后做的
//修改员工
//1.我们是按钮创建之前就绑定了click,所以绑定不上事件
//2.1.我们可以在创建按钮的时候绑定,但是很麻烦
//2.2.我们可以live函数,但是新版jquery已经废弃了这个函数,使用on进行替代
$(document).on("click",".edit_btn",function (){
//首先查出员工的信息,显示要修改的员工的信息
//查出部门信息,并显示部门列表
getDepts("#updateModel select");
getEmp($(this).attr("edit-id"));//获取刚才给新增按钮添加的自定义属性,里面存放员工id
//3.把员工的id传递给模态框的更新按钮
$("#update_btn").attr("edit-id",$(this).attr("edit-id"));
$("#updateModel").modal({
backdrop:"static"
})
})
//查询员工信息的方法
//思路: 获取到员工id--->去数据库中查找对应员工,返回给前端---->前端获取数据,直接将数据回显在对应的位置
function getEmp(id)
{
$.ajax({
url:"${ctx}/emp/"+id,
type:"get",
success:function (result){
var empData=result.extent.emp;
$("#UpdateEmpName").text(empData.empName);
$("#updateEmail3").val(empData.email);//给输入框赋值,直接往val里面填入参数即可
$("#updateModel input[name=gender]").val(empData.gender);
$("#updateModel select").val(empData.dpId);
}
})
}
//点击更新,更新员工信息
$("#update_btn").click(function (){
//验证邮箱是否合法
//校验邮箱
var email=$("#updateEmail3").val();
var regEmail=/^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/;
if(!regEmail.test(email))
{
show_valid_msg("#updateEmail3","error","邮箱格式有误");
return false;
}
else
{
show_valid_msg("#updateEmail3","success","");
}
//2.发送ajax请求保存更新的员工数据
$.ajax({
//这里把员工id传递到更新按钮上,这样这里的id参数就可以直接从按钮上获得
url:"${ctx}/emp/"+$(this).attr("edit-id"),
type:"put",
data:$("#updateModel form").serialize(),//表单序列化,这里是rest风格的url,因此请求参数中要附带一个_method参数
success:function (res){
//1.关闭模态框
$("#updateModel").modal("hide");
//2.回到主页面
toPage(curNum);
}
})
})
//删除某个员工
//给删除按钮搞上点击事件---注意使用on函数
$(document).on("click",".del_btn ",function (){
//1.弹出确认删除的对话框
//获取要删除的人的名字
var empName=$(this).parents("tr").find("td:eq(2)").text()
//获取要删除的员工的id---通过在创建删除按钮的时候,直接添加一个属性记录当前id即可
var empId=$(this).attr("del-id");
if(confirm("确认删除"+empName+"员工吗?"))
{
//确认,发送ajax请求
$.ajax({
url:"${ctx}/emp/"+empId,
type:"delete",
success:function (res){
//回到本页
toPage(curNum);
}
})
}
})
//完成全选和全不选的功能
$("#check-all").click(function (){
//attr获取check是undefined
//对于原生的dom属性建议使用prop获取,而attr用来获取自定义属性的值
//使用prop修改和读取dom原生属性的值
$(".check-item").prop("checked",$(this).prop("checked"));
});
//此处不能直接绑定click的原因是因为,发送ajax请求动态往标签体内增加内容是在页面加载完成以后做的
//如果单个选择框当前页全部选满了,那么最大的选择框也会被选中
$(document).on("click",".check-item",function (){
var flag=$(".check-item:checked").length==$(".check-item").length;
$("#check-all").prop("checked",flag);
})
//点击全部删除按钮,就进行批量删除
$("#del_btn").click(function ()
{
var empNames="";
var del_idstr="";//组装员工id的字符串
//遍历单个被选中的选择框
$.each($(".check-item:checked"),function (){
//当前正在遍历的item
//获取每个单选框对应的员工名字
empNames+=$(this).parents("tr").find("td:eq(2)").text()+",";
//组装员工id的字符串
del_idstr+=$(this).parents("tr").find("td:eq(1)").text()+"-";
});
//去除empNames字符串最后一个多余的逗号
empNames=empNames.substring(0,empNames.length-1);//最后一个多余的逗号去掉
//取出组装id得到的字符串的最后一个-
del_idstr=del_idstr.substring(0,del_idstr.length-1)
if(confirm("确认删除"+empNames+"等员工吗?"))
{
//发送ajax请求删除
$.ajax({
url:"${ctx}/emp/"+del_idstr,
type:"DELETE",
success:function (res){
//回到当前页面
toPage(curNum);
}
})
}
});
</script>
</body>
</html>
POJO层除了逆向工程自动生成的四个类外,还额外增添了一个消息类msg,将其作为json字符串返回给前端,并且使用链式编程和静态函数,方便直接调用
package Com.POJO;
import java.util.HashMap;
import java.util.Map;
//通用的json返回类
public class msg
{
//状态码 100--成功 200---失败
private int code;
//提示信息
private String msg;
//用户要返回的给浏览器的数据放在map中
private Map<String,Object> extent=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.getExtent().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> getExtent() {
return extent;
}
public void setExtent(Map<String, Object> extent) {
this.extent = extent;
}
}
Dao层就是逆向工程自动生成的三个接口
注意:Dao层对应的三个逆向工程自动生成的.xml文件中,我们还需要增添两个查出员工的同时查出部门信息的方法,一个是单个员工,一个是多个员工,这里使用的是联合查询里面的左连接
<!--查出员工表的同时,查出部门信息-->
<resultMap id="DeptResultMap" type="Com.POJO.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="dp_id" jdbcType="INTEGER" property="dpId" />
<!--使用association封装查询出来的自定义对象-->
<association property="department" javaType="Com.POJO.department">
<id column="dep_id" property="depId"/>
<result column="dep_name" property="depName"/>
</association>
</resultMap>
<sql id="WithDept_column_list">
emp_id, emp_name, gender, email,dep_id,dep_name
</sql>
<select id="selectByExampleWithDepartmnet" resultMap="DeptResultMap">
select
<if test="distinct">
distinct
</if>
<include refid="WithDept_column_list" />
/*联合查询需要对此处进行修改*/
FROM employee LEFT JOIN department ON dep_id=dp_id
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
<if test="orderByClause != null">
order by ${orderByClause}
</if>
</select>
<select id="selectByPrimaryKeyWithDepartmnet" parameterType="java.lang.Integer" resultMap="DeptResultMap">
select
<include refid="Base_Column_List" />
FROM employee LEFT JOIN department ON dep_id=dp_id
</select>
service层—两个类,一个处理部门的数据库操作,一个处理员工的数据库操作
deptService:
package Com.service;
import Com.Dao.departmentMapper;
import Com.POJO.department;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class deptService {
@Autowired
private departmentMapper dm;
public List<department> getDepets()
{
return dm.selectByExample(null);//没有筛选条件,就是全部查出
}
}
EmployueeService:
package Com.service;
import Com.Dao.employeeMapper;
import Com.Dao.userMapper;
import Com.POJO.departmentExample;
import Com.POJO.employee;
import Com.POJO.employeeExample;
import Com.POJO.user;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class EmployueeService {
@Autowired
employeeMapper em;
@Autowired
userMapper um;
//查询所有员工
public List<employee> getAll()
{
//参数是筛选条件
return em.selectByExampleWithDepartmnet(null);
}
//获取登录的信息
public user getUser()
{
return um.getUser();
}
//保存员工
public int saveEmp(employee e)
{
//这里不插入id,因此是有选择的插入
return em.insertSelective(e);
}
//后端检验用户名是否可用
public boolean checkEmp(String empName)
{
//判断用户名在数据库中有几个
employeeExample ee=new employeeExample();
employeeExample.Criteria criteria=ee.createCriteria();
criteria.andEmpNameEqualTo(empName);
long count=em.countByExample(ee);
return count==0;//如果用户名只有一个,为真,否则为假
}
public employee getEmp(Integer id)
{
return em.selectByPrimaryKey(id);
}
public void updateEmp(employee e) {
em.updateByPrimaryKeySelective(e);//根据主键有选择的更新---例如员工名字不可修改,因此封装的对象的名字为null
}
public void deleteEmp(Integer id) {
em.deleteByPrimaryKey(id);
}
public void deleteBatch(List<Integer> ids)
{
employeeExample ee=new employeeExample();
employeeExample.Criteria criteria = ee.createCriteria();
criteria.andEmpIdIn(ids);//要批量删除的id位于某个集合内
em.deleteByExample(ee);//按照条件删除
}
}
controller层对应也有两个类,一个处理部门请求,一个处理员工请求
DeptController:
package Com.Controller;
import Com.POJO.department;
import Com.POJO.msg;
import Com.service.deptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
/*
* 处理和部门有关的请求
* */
@Controller
public class DeptController
{
@Autowired
private deptService ds;
//查出所有部门信息
@RequestMapping("/depts")
@ResponseBody
public msg getDepts()
{
return msg.success().add("depts",ds.getDepets());
}
}
EmployeeController:
@Controller
public class EmployeeController {
@Autowired
EmployueeService es;
//跳转到首页
@RequestMapping("/emp")
public String getEmps()
{
return "list";
}
@RequestMapping("/tolist")
public String toList(user u)
{
System.out.println("tolist");
user admin = es.getUser();
if(u.getName().equals(admin.getName())&&u.getPassword().equals(admin.getPassword()))
return "redirect:/emp";
return "redirect:/index.jsp";
}
//使用ajax技术,显示信息
@RequestMapping("/emps")
@ResponseBody//这里需要导入json的依赖
public msg getEmpWithPageinfo(@RequestParam(value = "pn",defaultValue = "1") Integer pn, Model model)
{
//传入当前显示的页码和每页显示记录的条数,查询语句紧跟在后面
PageHelper.startPage(pn,5);
List<employee> emps = es.getAll();
//使用pageINfo包装查询后的结果
//封装了详细的分页信息,包括查询出来的数据,传入连续显示的页数
PageInfo page=new PageInfo(emps,5);
model.addAttribute("info",page);
System.out.println("emps");
//以json形式返回---包含info信息和失败成功信息
return msg.success().add("info",page);
}
//保存员工的方法:保存之前进行后端校验,因为有方法可以绕过前端校验,因此后端校验必须要有
//后端校验是在点击保存按钮,数据库返回给数据库后,进行校验,然后才会显示相关错误信息
//与前端你写错后,立马提示你有错误不同
@RequestMapping(value = "/emp",method = RequestMethod.POST)
@ResponseBody
//@Valid注解,告诉SpringMVC,封装这个javabean对象时,按照这个对象里面变量规定的校验规则进行校验
//javaBean后面紧跟一个BindingResult,这个BindingResult就是封装前一个bean的校验结果
public msg saveEmp(@Valid employee e,BindingResult result)
{
//后端校验用户名和邮箱格式是否正确
if(result.hasErrors())
{
//校验失败,应该返回失败,在模态框中显示校验失败的错误信息
List<FieldError> errors = result.getFieldErrors();
Map<String ,Object> map=new HashMap<String, Object>();
for(FieldError e1:errors)
{
System.out.println("错误的字段名:"+e1.getField());
System.out.println("错误信息:"+e1.getDefaultMessage());
map.put(e1.getField(),e1.getDefaultMessage());
}
return msg.fail().add("errorFields",map);
}
//这里添加一个后端校验用户名是否重复
//校验用户名是否重复
if(!es.checkEmp(e.getEmpName()))
{
return msg.fail().add("errorName","后端校验发现用户名不可用");
}
es.saveEmp(e);
return msg.success();
}
//验证用户名是否可用的方法
@RequestMapping("/checkEmp")
@ResponseBody
public msg checkEmp( String empName)//
{
//先判断用户名是否是合法表达式
String regx="(^[a-zA-Z0-9_-]{6,16}$)|(^[\\u2E80-\\u9FFF]{2,5}$)";
if(!empName.matches(regx))
{
return msg.fail().add("va_msg","用户名必须是6-16位数字和字母的组合或者是2-5位的中文");
}
//数据库用户名重复校验
boolean ret = es.checkEmp(empName);
if(ret)
return msg.success();
return msg.fail().add("va_msg","用户名不可用");
}
//查询员工
@RequestMapping(value = "/emp/{id}",method = RequestMethod.GET)
@ResponseBody
public msg getEmp(@PathVariable("id") Integer id)
{
employee e=es.getEmp(id);
return msg.success().add("emp",e);
}
//员工更新方法
@RequestMapping(value = "/emp/{empId}",method = RequestMethod.PUT)
@ResponseBody
public msg updateEmp(employee e)
{
es.updateEmp(e);
return msg.success();
}
//员工删除方法实现
@RequestMapping(value = "/emp/{id}",method = RequestMethod.DELETE)
@ResponseBody
public msg deleteEmpById(@PathVariable("id")String ids)
{
System.out.println("======================================");
if(ids.contains("-"))//批量删除
{
System.out.println("批量删除中");
String[] str_ids = ids.split("-");
//调用批量删除的方法
//组装id集合
List<Integer> del_ids=new ArrayList<Integer>();
for(String s:str_ids)
{
int i = Integer.parseInt(s);
del_ids.add(i);
}
es.deleteBatch(del_ids);
}
else//单个删除
{
Integer id=Integer.parseInt(ids);
es.deleteEmp(id);
}
return msg.success();
}
}
总结
ajax和js使用注意事项
- 实现某个功能时,尽量抽取成一个方法,提高重用性,例如本项目中跳转到某一页的方法,和抽取出来的显示校验结果的方法
- 如果某个值需要在多处使用,那么可以定义一个全局变量保存该值,方便调用
- 使用ajax向标签中追加内容后,标签体中不会显示出现追加的内容,但是实际已经存在,那么下一次再次调用ajax时,又会重复上一次的追加行为,那么页面效果就是内容重复追加,解决办法就是在每次调用ajax之前,先将之前重复追加的内容清除掉
- 同理如果ajax是追加或者修改了标签的属性,那么对应的被更改的属性就会一直存在,因此下一次调用ajax之前,需要先清除之前追加给标签的属性
- 我们可以通过给按钮或者其他控件添加自定义属性的方式,来保存一些我们需要用到的数据,例如给删除按钮增添一个自定义属性保存当前员工的id,方便一会通过在按钮点击事件中获取到id值,从而通过ajax返回给服务器端,进行删除逻辑操作
- 使用ajax时,在获取到服务器端发送来的数据后,可以在成功的回调函数中,获取数据,然后通过append等方式,动态向需要的标签或位置中添加内容