1.数据库准备

部门tbl_dept

员工tbl_emp

建立员工和部门的外键

2.在IDEA创建SSM项目环境

2.1配置Web模块

最上面的图是错误示范,注意!!! 在Tomcat配置了项目路径,就不需要再webapp这里配置项目路径,不然是找不到这里面的资源的!!!!!!!

2.2 引入Maven的SSM相关依赖

    <build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin> <!-- mvn mybatis-generator:generate -->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.2</version>
<configuration>
<verbose>true</verbose>
<overwrite>true</overwrite>
<!--这个是指代从哪里加载generatorConfig文件-->
<configurationFile>
src/main/resources/generator/generatorConfig.xml
</configurationFile>
</configuration>
<dependencies>
<!-- 数据库驱动 -->
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
</dependencies>
</plugin>
</plugins> </build> <properties>
<project.build.source>UTF-8</project.build.source>
<!--版本同一管理-->
<mybatis.version>3.2.8</mybatis.version>
<mybatis.spring.version>1.2.2</mybatis.spring.version>
<mysql.version>5.1.32</mysql.version>
</properties> <dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>3.2.17.RELEASE</version>
</dependency>
<!--spring-tx-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>3.2.17.RELEASE</version>
</dependency>
<!--jdbc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>3.2.1.RELEASE</version>
</dependency>
<!--面向切面AOP依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>3.1.0.RELEASE</version>
</dependency> <dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
</dependency> <!--连接池-->
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency> <!--mybatis-->
<!-- MyBatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>${mybatis.spring.version}</version>
</dependency> <!--日志用log4j-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency> <dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
</dependency> <dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency> <!--junit测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.9</version>
</dependency> </dependencies>

注意1:spring的相关依赖版本要一致,不然maven加载其他相关依赖的时候,会有不同版本的相同依赖也会加载进来,很可能会冲突。而且从maven仓库又下载就会很慢。

注意2: 当你在spring配置文件中死活都不出来某个标签的时候,很可能你在maven里导错包,首先需要重新导入正确的依赖。然后把这个xml上面错误的xsd约束删除掉,就可以了。

applicationContext-service.xml

    <context:component-scan base-package="cn.zhanp.ssm_crud.service"></context:component-scan>

    <!--配置事务-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean> <aop:config>
<aop:pointcut id="txPointCut" expression="execution(* cn.zhanp.ssm_crud.service..*(..))"></aop:pointcut>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"></aop:advisor>
</aop:config> <tx:advice id="txAdvice">
<tx:attributes>
<tx:method name="get*" read-only="true"/>
</tx:attributes>
</tx:advice>

注意点3:当你在IDEA中无法识别另外一个如applicationContext-dao中的数据源的引用bean时,应该在IDEA的spring Module中把spring配置文件都添加进去,同一个applicationContext就可以识别了

2.3 部署到Tomcat

2.4 配置MBG插件,逆向工程

            <!-- mvn mybatis-generator:generate -->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.2</version>
<configuration>
<verbose>true</verbose>
<overwrite>true</overwrite>
<configurationFile>src/main/resources/generator/generatorConfig.xml</configurationFile>
</configuration>
<dependencies>
<!-- 数据库驱动 -->
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
</dependencies>
</plugin>

2.5 编写SSM各层的配置文件和数据库文件以及日志文件

springmvc.xml

    <!--加载spring的配置文件-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:</param-value>
</context-param>
<!--监听器,加载context-param里的配置文件-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener> <servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<!--加载springmvc的配置文件-->
<param-name>contextConfigLocation</param-name>
<param-value>classpath:</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>

总结:别忘了监听器,不配置监听器,根本就没人去帮你加载那几个配置文件

mybatis-config.xml

<configuration>

    <settings>
<!--全局下划线转驼峰命名-->
<setting name="mapUnderscoreToCamelCase" value="true"></setting>
<!-- mybatis3的 SQL日志打印方式 -->
<!--<setting name="logImpl" value="log4j"/>-->
</settings> <!--其余配置都在spring的xml中配置,通过属性注入到sqlSessionFactory的bean中,不在这里写-->
</configuration>

applicationContext-dao.xml

    <!--配置数据源-->
<context:property-placeholder location="classpath:db/db.properties"></context:property-placeholder> <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> <!-- c3p0连接池的私有属性 -->
<property name="maxPoolSize" value="30" />
<property name="minPoolSize" value="10" />
<!-- 关闭连接后不自动commit -->
<property name="autoCommitOnClose" value="false" />
<!-- 获取连接超时时间 -->
<property name="checkoutTimeout" value="10000" />
<!-- 当获取连接失败重试次数 -->
<property name="acquireRetryAttempts" value="2" />
</bean> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="typeAliasesPackage" value="cn.zhanp.ssm_crud.model"></property>
<property name="configLocation" value="classpath:mybatis/mybatis-config.xml"></property>
<property name="mapperLocations" value="classpath:mapper/*.xml"></property>
</bean> <!--注册一个特殊的sqlSession用于测试的时候支持批量插入-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg ref="sqlSessionFactory"></constructor-arg>
<constructor-arg value="BATCH"></constructor-arg>
</bean> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
<property name="basePackage" value="cn.zhanp.ssm_crud.dao"></property>
</bean>
</beans>

总结:sqlSessionTemplate,用于注册一些特殊的sqlSession

    public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) {
this(sqlSessionFactory, executorType, new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), true));
}

总结2: error-------通配符的匹配很全面, 但无法找到元素 'aop:config' 的声明。

解决:依旧是把xsd依赖重新写入(一定要注意mvc和aop这些的xsd约束引入是否正确)

总结3: 提示Error creating bean with name 'org.springframework.cache.interceptor.CacheInterceptor

又是xsd依赖引入错误

2.6 把依赖添加入到Artifact中

3.Spring单元测试模拟数据测试mapper

总结:

<!--在spring单元测试中,由于引入validator而导致的Tomcat7及以下的EL表达式版本不一致-->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-el-api</artifactId>
<version>8.5.24</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jasper-el</artifactId>
<version>8.5.24</version>
<scope>provided</scope>
</dependency>

4. 配置pageHelper,实现分页

参考官方文档:https://github.com/pagehelper/Mybatis-PageHelper/blob/master/wikis/zh/HowToUse.md

mybatis-config.xml

    <!--配置分页插件,也就是指定拦截器-->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
        <!--分页插件-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.2</version>
</dependency>

5. 分页的后台代码

    @RequestMapping(value = "/emps",method = {RequestMethod.GET,RequestMethod.POST})
public ModelAndView getEmps(@RequestParam(value = "pn",defaultValue = "1") Integer page_num){ // 实现分页,默认大小一页显示5条
PageHelper.startPage(page_num, 5); List<EmployeeCustom> list = employeeService.getAllWithDept(); // 用pageInfo去包装查询后的结果,第二种构造器,第二参数为,连续显示的页数(以当前页为中心,1,2,3,4,5这样)
PageInfo<EmployeeCustom> pageInfo = new PageInfo<>(list,5); ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("empList"); modelAndView.addObject("pageInfo",pageInfo); return modelAndView;
}

总计:

  1. 在controller的方法里 要有一个@RequestParam(value="",defaultValue="") xxx

    要求从前端传入第几页pageNumber, 又或者传两个,一个是页码,一个是每页的数目size,

    因为我们很可能会首页就跳转到这里查看,所以很可能没携带页码参数,所以设置一个默认值

    defaultValue,这样就可以实现了。

  2. PageHelper.startPage(page_num, 5);

    用静态方法去让mybatis设置的pageHelper的拦截器对紧跟着的第一个select()方法进行分页,并设置size

  3. PageInfo pageInfo = new PageInfo<>(list,5);

    用pageInfo 来包装查询后的结果,这样可以把结果包装进pageInfo对象中,

    并且第二个参数是navigatepageNums: 是以当前分页为基数,计算导航页码的个数

    举个例子:

    比如当前页码为1,那么navigatepageNums五个,就是1,2,3,4,5。

    当前页码为5,那么navigatepageNums五个,就是3,4,5,6,7 尽可能地以5为中心展开分页导航

    并且能够获取相关分页的其他参数,比如total啊,判断是不是首页啊,有没有上一页,下一页啊这些方法。

    //PageInfo包含了非常全面的分页属性
    assertEquals(1, page.getPageNum());
    assertEquals(10, page.getPageSize());
    assertEquals(1, page.getStartRow());
    assertEquals(10, page.getEndRow());
    assertEquals(183, page.getTotal());
    assertEquals(19, page.getPages());
    assertEquals(1, page.getFirstPage());
    assertEquals(8, page.getLastPage());
    assertEquals(true, page.isFirstPage());
    assertEquals(false, page.isLastPage());
    assertEquals(false, page.isHasPreviousPage());
    assertEquals(true, page.isHasNextPage());

6. bootstrap后台界面显示

总结的小问题:一开始不出现字体图标,为什么呢?

答:因为我只在项目中引入了bootstrap的css和js,而字体图标虽然是以class来标识,但是底层实现是要根据

fonts来匹配图标文件的,后缀为这种:

所以要引入bootstrap的fonts文件,并且和js文件夹在同等级目录结构

注意:SpringMVC静态文件的配置,一定要配置过滤器,不要拦截这个

    <servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<!--加载springmvc的配置文件-->
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:spring/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> <servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.js</url-pattern>
<url-pattern>*.css</url-pattern>
<url-pattern>/assets/*"</url-pattern>
<url-pattern>/images/*</url-pattern>
<url-pattern>/static/*</url-pattern>
</servlet-mapping>

作用:在web.xml文件中经常看到这样的配置default,这个配置的作用是:对客户端请求的静态资源如图片、JS文件等的请求交由默认的servlet进行处理,也就是不经过DispatcherServlet的拦截

        <div class="row">
<table class="table table-hover">
<tr>
<th>#</th>
<th>empName</th>
<th>gender</th>
<th>email</th>
<th>deptName</th>
<th>操作</th>
</tr>
<c:forEach var="page" items="${pageInfo.list}">
<tr>
<td>${page.empId}</td>
<td>${page.empName}</td>
<td>${page.gender}</td>
<td>${page.email}</td>
<td>${page.department.deptName}</td>
<td>
<button type="button" class="btn btn-sm btn-primary">
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span>
修改
</button>
<button type="button" class="btn btn-sm btn-danger">
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span>
删除
</button>
</td>
</tr>
</c:forEach>
</table>
</div>
<div class="row">
<div class="col-md-6">
当前总记录数:${pageInfo.total}
</div>
<div class="col-md-6" style="text-align: center">
<nav class="pagination">
<ul class="pagination">
<li>
<a href="${pageContext.request.contextPath}/emps?pn=1">首页</a>
</li>
<%--判断上一页是否可用--%>
<c:if test="${pageInfo.hasPreviousPage==true}">
<li>
<a href="${pageContext.request.contextPath}/emps?pn=${pageInfo.prePage}" aria-label="Previous">
<span aria-hidden="true">&laquo;</span>
</a>
</li>
</c:if> <c:forEach items="${pageInfo.navigatepageNums}" var="pageNum">
<li>
<a href="${pageContext.request.contextPath}/emps?pn=${pageNum}">${pageNum}</a>
</li>
</c:forEach>
<c:if test="${pageInfo.hasNextPage==true}">
<li>
<a href="${pageContext.request.contextPath}/emps?pn=${pageInfo.nextPage}" aria-label="Previous">
<span aria-hidden="true">&raquo;</span>
</a>
</li>
</c:if>
<li>
<a href="${pageContext.request.contextPath}/emps?pn=${pageInfo.pages}">末页</a>
</li>
</ul>
</nav>
</div>
</div>

7.统一JSON 响应类的编写

前提:

  1. 为什么要JSON?因为可以针对不同的前端,不管是浏览器,小程序,安卓移动端等等,都可以通过JSON

数据来达到数据交互的效果。而不用拘泥于以前的浏览器页面Html和jsp这些。

  1. 有了JSON,就可以在实际项目中快速分工。后端传JSON数据,前端通过Ajax向后端提供的接口获取JSON数据,解析JSON数据,渲染进页面中,即可完成交互。而不需要像以前那样,通过JSP等后端模板引擎,来在服务端渲染。造成前后端的开发耦合。
//    要导入jackson包,SpringMVC的ResponseBody才能起作用

    @RequestMapping(value = "/emps",method = {RequestMethod.GET,RequestMethod.POST})
@ResponseBody
public Msg getEmpsWithJson(@RequestParam(value = "pn",defaultValue = "1") Integer page_num){
// 实现分页,默认大小一页显示5条
PageHelper.startPage(page_num, 5); List<EmployeeCustom> list = employeeService.getAllWithDept(); // 用pageInfo去包装查询后的结果,第二种构造器,第二参数为,连续显示的页数(以当前页为中心,1,2,3,4,5这样)
PageInfo<EmployeeCustom> pageInfo = new PageInfo<>(list,5); // 封装进JSON 响应类
Msg msg = Msg.success().add("pageInfo", pageInfo); return msg;
}
public class Msg {
private int code; //代码
private String msg; //信息 //用户要返回给浏览器的数据,最好用一个map对象,前端可以直接取出这个map['key']这样的方式来取出需要的数据
//而且设置为Map的好处,通过返回Msg对象,可以进行链式添加,map中有多个键值对,可以传多个对象
private Map<String,Object> extend = new HashMap<>(); //返回Msg对象,进行链式操作
public static Msg success(){
Msg msg = new Msg();
msg.setCode(200);
msg.setMsg("处理成功");
return msg;
} public static Msg fail(){
Msg msg = new Msg();
msg.setCode(500);
msg.setMsg("处理失败");
return msg;
} //添加要返回的主体数据(先通过success和fail,拿到Msg,就可以进行链式操作了)
public Msg add(String key,Object value){
this.getExtend().put(key,value);
return this;
}
//此处省略getter,setter

8.用vue重构Jquery拼接的前端分离页面

window.baseURL = "http://localhost:8080/ssm_crud/"

$(document).ready(function () {
window.empList = new Vue({
el:"#empList",
data:{
emps:[]
},
method:{ },
created(){
window.getEmpsIndex();
// this.$forceUpdate() ;
} })
}) function getEmpsIndex() {
console.log(baseURL+"emps");
$.get(baseURL+"emps",
{
pn:1
},
function (result) {
//一定要加上设置为window.xxx ,不然他就是一个新的引用,而不是上面的vue对象
window.empList.emps = result.extend.pageInfo.list;
});
}

总结:

  1. 为了让页面一开始就触发函数,获取首页数据,需要用到vue的生命周期函数

  2. 而且需要注意到JS的作用域,如果写成empList.emps = result.extend.pageInfo.list;

    而不是window.empList.emps,那么JS就会新建一个empList对象,这样vue对象实际上没有获取到值。

    这也是我以前用的方法,用window,保证操作的是同一个vue对象。

  3. 上面的方法的原因其实是:我忘记了ES5的特性,因为在里面的那个function函数中,this已经指向了全局变量window, 而不是当前的vue实例了,所以设置不了data值。

    要用let self = this来保存this的引用,又或者用ES6的箭头函数

        methods:{
getEmps:function (pn) {
$.get(baseURL+"emps",
{
pn:pn
},
function (result) {
console.log(this); //查看可知这个this又指向了window对象 self.emps = result.extend.pageInfo.list;
self.pageInfo = result.extend.pageInfo; console.log("pages"+self.pageInfo.pages); //测试
console.log("pn"+pn);
});
}
}
    <div id="empList" class="container">
<h1>SSM_CRUD</h1>
<div class="row" style="text-align: center">
<div class="col-md-offset-8">
<button class="btn btn-info">新增</button>
<button class="btn btn-danger">删除</button>
</div>
</div>
<div class="row">
<table class="table table-hover">
<tr>
<th>#</th>
<th>empName</th>
<th>gender</th>
<th>email</th>
<th>deptName</th>
<th>操作</th>
</tr>
<!--用vue重构列表渲染-->
<!--<c:forEach var="page" items="${pageInfo.list}">-->
<tr v-for="emp in emps">
<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-sm btn-primary">
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span>
修改
</button>
<button type="button" class="btn btn-sm btn-danger">
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span>
删除
</button>
</td>
</tr>
<!--</c:forEach>-->
</table>
</div>
<!--分页界面代码-->
<div class="row">
<div class="col-md-6">
当前总记录数:{{pageInfo.total}}
</div>
<div class="col-md-6" style="text-align: center">
<nav class="pagination">
<ul class="pagination">
<li>
<span @click="getEmps(1)">首页</span>
</li>
<li v-if="pageInfo.hasPreviousPage">
<span aria-hidden="true" @click="getEmps(pageInfo.prePage)">&laquo;</span>
</li>
<li v-for="pageNum in pageInfo.navigatepageNums">
<span @click="getEmps(pageNum)">{{pageNum}}</span>
</li>
<li v-if="pageInfo.hasNextPage">
<span aria-hidden="true" @click="getEmps(pageInfo.nextPage)">&raquo;</span>
</li>
<li>
<span @click="getEmps(pageInfo.pages)">末页</span>
</li>
</ul>
</nav>
</div>
</div>
</div>

实现选中当前分页,当前分页高亮加深的效果。

                        <li v-for="pageNum in pageInfo.navigatepageNums" :class="{active:pageNum==pageInfo.pageNum}">
<span @click="getEmps(pageNum)">{{pageNum}}</span>
</li>

总结 : 动态class的语法一定要加{}

9. 添加员工

简介:通过以模态框的方式来编写 添加员工的界面。 在点击新增按钮后,让模态框显示。

怎么做出来的:通过bootstrap官方文档,拉取组件样式和 模态框model 的JS插件来完成这个界面。

细节:部门名称:是以下拉菜单的形式,所以要ajax获取部门数据。

总结:

  1. 因为有些我不用完全的form表单提交的方式,比如部门数据,是用的ul配li,所以在JS里面获取前端的所有表单数据,然后再手动submit上去校验。也不难,用下JQuery获取值和DOM结点即可

  2. 需要注意的技巧是:

    1. 获取单选按钮选中后的value值,用$('input[name="gender"]:checked').attr('value');

    2. 获取下拉菜单(但又不是select这种写法)选中的值,view层的绑定事件

          <ul class="dropdown-menu" aria-labelledby="dLabel">
      <!--要获取部门数据id,和name-->
      <li v-for="dept in depts" class="pointer" @click="getSelectedDept(dept.deptId,dept.deptName)">
      <span>{{dept.deptName}}</span>
      </li>
      </ul>
    3. 选择部门下拉菜单我是以按钮的形式

                <button class="btn btn-sm btn-primary" id="dedt_btn"
      type="button" data-toggle="dropdown"
      aria-haspopup="true" aria-expanded="false">
      选择部门
      <span class="caret"></span>
      </button>

配合JS

              //获取选择的部门id
getSelectedDept:function (dept_id,dept_name) {
this.selected_dept_id = dept_id;
//按钮提示的回显
$("#dedt_btn").text(dept_name);
console.log(this.selected_dept_id)
},
```

界面代码:

    <style type="text/css">
li span{
/*设置悬浮可点*/
cursor: pointer;
}
#add_emp li:hover{
display: block;
background: #8c8c8c;
padding: 10px;
/*设置悬浮可点*/
cursor: pointer;
}
</style> <!-- Modal -->
<div class="modal fade" id="add_emp" tabindex="-1" role="dialog" aria-labelledby="add_empLabel">
<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">&times;</span></button>
<h4 class="modal-title" id="myModalLabel">添加员工</h4>
</div>
<div class="modal-body">
<form class="form-horizontal">
<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="name" placeholder="name">
</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="M"> 男
</label>
<label class="radio-inline">
<input type="radio" name="gender" value="F"> 女
</label>
</div>
</div>
<div class="form-group">
<label for="inputEmail" class="col-sm-2 control-label">Email</label>
<div class="col-sm-10">
<input type="email" class="form-control" id="inputEmail" placeholder="Email">
</div>
</div>
<!--下拉菜单-->
<div class="form-group">
<label for="inputEmail" class="col-sm-2 control-label">选择部门</label>
<div class="dropdown col-sm-10">
<button class="btn btn-sm btn-primary" id="dedt_btn" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
选择部门
<span class="caret"></span>
</button>
<ul class="dropdown-menu" aria-labelledby="dLabel">
<!--要获取部门数据id,和name-->
<li v-for="dept in depts" class="pointer" @click="getSelectedDept(dept.deptId,dept.deptName)">
<span>{{dept.deptName}}</span>
</li>
</ul>
</div>
</div>
</form>
</div>
<div class="form-group">
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" @click="add_emp_submit">提交</button>
</div>
</div>
</div>
</div>
</div>
            //切换模态框的显示
add_emp_toggle:function () {
this.getDepts();
$("#add_emp").modal({
//设置 点击除模态框以外的其他地方,不会让模态框消失掉
backdrop:"static"
});
},
//获取部门信息
getDepts:function () {
let self = this;
$.get(baseURL+"depts",
function (result) {
self.depts = result.extend.depts;
})
},
//获取选择的部门id
getSelectedDept:function (dept_id,dept_name) {
this.selected_dept_id = dept_id;
//按钮提示的回显
$("#dedt_btn").text(dept_name);
console.log(this.selected_dept_id)
},
//增加员工 提交
add_emp_submit:function () {
let empName = $('#inputName').val();
let gender = $('input[name="gender"]:checked').attr('value');
let email = $('#inputEmail').val();
let dId = this.selected_dept_id; console.log(empName)
console.log(gender)
console.log(email)
console.log(dId) $.post(baseURL+"/emps/add",
{
empName:empName,
gender:gender,
email:email,
dId:dId
},
function (result) {
if(result.code==200){
//重新获取第一页数据,刷新页面
window.getEmpsIndex();
$("#add_emp").modal('hide');
}
})
}

添加员工 提交数据后 回显最后一页的实现:

                    //这个是添加员工的提交后的 回调函数
function (result) {
if(result.code==200){
//获取最后一页数据,刷新页面
let lastPage = result.extend.lastPage;
console.log("lastPage:"+lastPage)
self.getEmps(lastPage);
$("#add_emp").modal('hide');
}
})
    //要拿到最后一页
@Override
public int getLastPage(int size) {
EmployeeExample employeeExample = new EmployeeExample();
EmployeeExample.Criteria criteria = employeeExample.createCriteria();
criteria.andEmpIdIsNotNull();
int num = employeeMapper.countByExample(employeeExample); if(num%size==0){
return num/size;
}else{
return num/size+1;
}
}
    @RequestMapping(value = "/emps/add",method = RequestMethod.POST)
@ResponseBody
public Msg addEmp(Employee employee){
employeeService.addEmployee(employee); //要拿到最后一页
//总员工数
int lastPage = employeeService.getLastPage(5); return Msg.success().add("lastPage",lastPage);
}

9.1 添加员工的校验状态

名字和邮箱的校验和前端显示

前端校验页面的用法

bootstrap摘录:

<div class="form-group has-success">
<label class="control-label" for="inputSuccess1">Input with success</label>
<input type="text" class="form-control" id="inputSuccess1" aria-describedby="helpBlock2">
<span id="helpBlock2" class="help-block">A block of help text that breaks onto a new line and may extend beyond one line.</span>
</div>
<div class="form-group has-warning">
<label class="control-label" for="inputWarning1">Input with warning</label>
<input type="text" class="form-control" id="inputWarning1">
</div>
<div class="form-group has-error">
<label class="control-label" for="inputError1">Input with error</label>
<input type="text" class="form-control" id="inputError1">
</div>
            //模态框显示的事件中
add_emp_toggle:function () {
this.getDepts();
$("#add_emp").modal({
//点击背景不删除
backdrop:"static"
}); /**
* 前端JQuery校验
* 1.用户名交给后端检查校验正则表达式,再查看是否重名
* 2.其他的前端校验正则即可
*/
$("#inputName").focusout(function () {
let self = this;
let name = $(this).val();
$.post(baseURL+"namechecked",{
empName:name
},
function (result) {
//因为修改的时候,是添加样式,所以可能重复has-success和has-error一起,是不正确的
$(self).parent().removeClass("has-success has-error");
$(self).next().text("");
if(result.code==200){ window.empList.vali_name = true;
//说明成功,正则匹配,且用户名可用
$(self).parent().addClass("has-success");
let msg = result.extend.message;
$(self).next().text(msg); }else{
window.empList.vali_name = false; //说明不成功,正则不匹配或者用户名不可用,要拿出错误信息,添加相关样式,以及不许提交
console.log($(this));
$(self).parent().addClass("has-error");
let msg = result.extend.message;
$(self).next().text(msg);
}
});
}); //正则表达式判断其他表单参数
$("#inputEmail").focusout(function () {
$(this).parent().removeClass("has-success has-error");
$(this).next().text("");
let email = $(this).val();
let regx = /^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.[a-zA-Z0-9]{2,6}$/ // 长度不限,可以使用英文(包括大小写)、数字、点号、下划线、减号,首字母必须是字母或数字;
console.log(regx.test(email));
if(regx.test(email)){
window.empList.vali_email = true; //符合
//1. 给父元素添加bootstrap 校验成功样式
$(this).parent().addClass("has-success") }else{
window.empList.vali_email = false;
//不符合
// 1. 给父元素添加bootstrap 校验错误样式
$(this).parent().addClass("has-error");
// 2. 在其下面添加span的提示内容
$(this).next().text("邮件格式错误!首字母必须是字母或数字");
}
});
},
    @RequestMapping(value = "/namechecked",method = RequestMethod.POST)
@ResponseBody
public Msg nameChecked(@RequestParam(value = "empName",required = true) String name){
//1. 先正则 2. 后判断是否重复 String regx = "^[[[\u4e00-\u9fa5]a-zA-Z0-9]+[-|a-zA-Z0-9._]$]{2,7}";
if(name.matches(regx)){
boolean nameChecked = employeeService.getNameChecked(name);
if(nameChecked){
Msg msg = Msg.success().add("message", "用户名可用");
return msg;
}else{
Msg msg = Msg.fail().add("message", "用户名不可用");
return msg;
}
}else{
Msg msg = Msg.fail().add("message", "请填写2-7位中文或者数字或英文");
return msg;
}
}

选择部门和提交表单时候的校验

            //获取选择的部门id
getSelectedDept:function (dept_id,dept_name) {
this.selected_dept_id = dept_id;
//按钮的回显
$("#dedt_btn").text(dept_name); //删掉span提示信息
$("#dept_span").text('');
console.log(this.selected_dept_id)
},
//增加员工 提交
add_emp_submit:function () {
let self = this; let empName = $('#inputName').val();
let gender = $('input[name="gender"]:checked').attr('value');
let email = $('#inputEmail').val();
let dId = this.selected_dept_id; if(self.selected_dept_id==''){
$("#dedt_btn").next().css("color","red");
$("#dedt_btn").next().text("请选择部门");
self.vali_dept = false;
}else{
self.vali_dept = true;
//全都校验成功,才可以提交
if(self.vali_name&&self.vali_dept&&self.vali_email){
$.post(baseURL+"emps/add",
{
empName:empName,
gender:gender,
email:email,
dId:dId
},
function (result) {
if(result.code==200){
//获取最后一页数据,刷新页面
let lastPage = result.extend.lastPage;
console.log("lastPage:"+lastPage)
self.getEmps(lastPage);
$("#add_emp").modal('hide'); //清空表单数据
$("#add_emp_form")[0].reset();
$("#add_emp_form").find("*").removeClass("has-error has-success");
$("#add_emp_form").find(".help-block").text('');
//清空校验状态位
self.vali_name = false;
self.vali_dept = false;
self.vali_email = false;
//清空选中的部门
self.selected_dept_id = "";
$("#dedt_btn").text("选择部门");
}
})
}else{
alert("请正确填写表单中的信息,再提交");
}
//提交表单 }
// console.log(empName)
// console.log(gender)
// console.log(email)
// console.log(dId) }

总结:校验页面的编写

  1. 用正则(前后端都可以,用后端进行重名校验

  2. 通过后端或者前端校验结束后拿到提示信息,根据匹配情况给前端页面设置样式(比如success,error)以及

    span里的提示信息

  3. 然后还要注意当重新聚焦到(通过聚焦focusOut()判断正则和重名)的时候,要注意先清空success,error的class样式,还有清空span里的提示信息

  4. 我是通过标志位判断每个表单参数是否匹配,都匹配了才可以提交Ajax请求提交表单。

  5. 记住:提交完表单后,要在回调函数里清空表单的数据,以及前端界面的一些校验提示的样式和提示信息。

    还要清空我设置的校验标志位,以及这里的下拉菜单选中的部门id

9.2JSR303后端校验

防君子,不防小人的前端JS校验,要通过后端的校验来保证表单参数的正确性。这个时候JSR303应运而生!

        <!--JSR303校验-->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.13.Final</version>
</dependency>

这里要注意点:如果使用Tomcat7及以下的版本,那么validator和Tomcat lib中的EL表达式语法是不匹配的。会提示找不到Class EL....之类的异常

解决方法:下载

往tomcat的lib中添加了el-api-3.0.0的jar包

public class Employee {
private Integer empId; @Pattern(regexp = "^[[[\\u4e00-\\u9fa5]a-zA-Z0-9]+[-|a-zA-Z0-9._]$]{2,7}",message = "请填写2-7位中文或者数字或英文")
private String empName; private String gender; @Pattern(regexp = "^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(\\.[a-zA-Z0-9-]+)*\\.[a-zA-Z0-9]{2,6}$",message = "邮件格式错误!首字母必须是字母或数字")
private String email;
    @RequestMapping(value = "/emps/add",method = RequestMethod.POST)
@ResponseBody
public Msg addEmp(@Valid Employee employee, BindingResult result) {
HashMap<String, Object> map = new HashMap<>();
//正则校验有误
if (result.hasErrors()) {
List<FieldError> errors = result.getFieldErrors();
for (FieldError error : errors) {
map.put(error.getField(), error.getDefaultMessage());
}
return Msg.fail().add("errorFields", map);
} //重名校验有误
String empName = employee.getEmpName();
boolean nameChecked = employeeService.getNameChecked(empName);
if (!nameChecked) {
map.put("empNameDuplicated", "已存在该用户名");
return Msg.fail().add("errorFields", map);
} else {
//校验正确
employeeService.addEmployee(employee); //要拿到最后一页
//总员工数
int lastPage = employeeService.getLastPage(5); return Msg.success().add("lastPage", lastPage);
}
}

如何拿到返回的后端校验返回的信息,去做其他的处理

            //增加员工 提交
add_emp_submit:function () {
let self = this; let empName = $('#inputName').val();
let gender = $('input[name="gender"]:checked').attr('value');
let email = $('#inputEmail').val();
let dId = this.selected_dept_id; if(self.selected_dept_id==''){
$("#dedt_btn").next().css("color","red");
$("#dedt_btn").next().text("请选择部门");
self.vali_dept = false;
}else{
//全都校验成功,才可以提交
if(self.vali_name&&self.vali_dept&&self.vali_email){
$.post(baseURL+"emps/add",
{
empName:empName,
gender:gender,
email:email,
dId:dId
},
function (result) {
//先进入后端校验,防君子,不防小人,所以需要后端JSR303校验
if(result.code==500){
//后端校验,有误
let error_emp_name = result.extend.errorFields.empName;
let error_emp_email = result.extend.errorFields.email;
let error_emp_name_duplicated = result.extend.errorFields.empNameDuplicated; //说明这个数据校验有误
if(error_emp_name!=undefined){
$("#inputName").parent().addClass("has-error");
$("#inputName").next().text(error_emp_name);
}
if(error_emp_email!=undefined){
$("#inputEmail").parent().addClass("has-error");
$("#inputEmail").next().text(error_emp_email);
}
if(error_emp_name_duplicated!=undefined){
$("#inputName").parent().addClass("has-error");
$("#inputName").next().text(error_emp_name_duplicated);
}
} else if(result.code==200){
//获取最后一页数据,刷新页面
let lastPage = result.extend.lastPage;
console.log("lastPage:"+lastPage)
self.getEmps(lastPage);
$("#add_emp").modal('hide'); //清空表单数据
$("#add_emp_form")[0].reset();
$("#add_emp_form").find("*").removeClass("has-error has-success");
$("#add_emp_form").find(".help-block").text('');
//清空校验状态位
self.vali_name = false;
self.vali_dept = false;
self.vali_email = false;
//清空选中的部门
self.selected_dept_id = "";
$("#dedt_btn").text("选择部门");
}
})
}else{
alert("请正确填写表单中的信息,再提交");
}

10. 修改员工

10.1 模态框

总结:我上面的添加员工 那个下拉菜单有问题,,,是自己写的,而不是用select组件,所以浪费了很多时间获取数据和绑定事件。下面对修改框做了调整

        <!-- 修改员工Modal -->
<div class="modal fade" id="update_emp_modal" tabindex="-1" role="dialog" aria-labelledby="update_empLabel">
<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">&times;</span></button>
<h4 class="modal-title">修改员工</h4>
</div>
<div class="modal-body">
<form class="form-horizontal" id="update_emp_form">
<div class="form-group">
<label class="col-sm-2 control-label">名字</label>
<div class="col-sm-10">
<p class="form-control-static" id="update_Name">{{emp.empName}}</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="M" :checked="'M'==emp.gender?'checked':''"> 男
</label>
<label class="radio-inline">
<input type="radio" name="gender" value="F" :checked="'F'==emp.gender?'checked':''"> 女
</label>
</div>
</div>
<div class="form-group">
<label for="update_Email" class="col-sm-2 control-label">Email</label>
<div class="col-sm-10">
<input type="email" class="form-control" name="email" id="update_Email" v-bind:value="emp.email">
<span class="help-block"></span>
</div>
</div>
<!--下拉菜单-->
<div class="form-group">
<label for="inputEmail" class="col-sm-2 control-label">选择部门</label>
<div class="col-sm-10">
<select class="form-control" name="dId">
<option v-for="dept in depts" v-bind:value="dept.deptId">{{dept.deptName}}</option>
</select>
</div>
</div>
</form>
</div>
<div class="form-group">
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" id="update_emp_button" @click="update_emp_submit(pageInfo.pageNum)">提交</button>
</div>
</div>
</div>
</div>
</div>

10.2 修改员工的JS

把校验函数封装一下,代码复用。增加,修改都可以用。通过传入相应的选择器即可。

            //封装前端校验邮件函数,通过传入相应的(输入框的)选择器
validate_email:function(ele){
//正则表达式判断其他表单参数
$(ele).focusout(function () {
$(ele).parent().removeClass("has-success has-error");
$(ele).next().text("");
let email = $(ele).val();
let regx = /^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.[a-zA-Z0-9]{2,6}$/ // 长度不限,可以使用英文(包括大小写)、数字、点号、下划线、减号,首字母必须是字母或数字;
console.log(regx.test(email));
if(regx.test(email)){
window.empList.vali_email = true; //符合
//1. 给父元素添加bootstrap 校验成功样式
$(ele).parent().addClass("has-success") }else{
window.empList.vali_email = false;
//不符合
// 1. 给父元素添加bootstrap 校验错误样式
$(ele).parent().addClass("has-error");
// 2. 在其下面添加span的提示内容
$(ele).next().text("邮件格式错误!首字母必须是字母或数字");
}
});
},

模态框的显示:包含了修改员工的信息的回显,以及表单校验,和模态框显示

            //修改模态框的显示
updateEmp:function(empId){
let self = this;
//获取该员工的信息
$.get(baseURL+"emps/"+empId,
function (result) {
self.emp = result.extend.emp;
let gender = result.extend.emp.gender;
let dId = result.extend.emp.dId;
console.log("gender:"+gender)
//给下拉菜单赋值
$("#update_emp select").val(dId); //要把empId传递到修改模态框的提交
$("#update_emp_button").attr("edit-id",empId);
}); //获取部门数据
this.getDepts(); //显示模态框
$("#update_emp_modal").modal({
backdrop:"static"
}); //表单校验
this.validate_email("#update_Email");
},

提交修改员工表单: 前后端遵循Restful API风格,修改用put请求

            update_emp_submit:function(pageNum){
let empId = $("#update_emp_button").attr("edit-id");
let self = this; //邮件格式符合
if(self.vali_email){ $.ajax({
url:baseURL+"emps/"+empId,
type:"PUT",
data:$("#update_emp_form").serialize(),
success:function (result) {
if(result.code==200){
//修改成功
//跳转到修改的员工的本页
self.getEmps(pageNum); //清掉表单数据以及状态位
self.vali_email = false;
$("#update_emp_modal").find('*').removeClass("has-success has-error");
$("#update_emp_modal .help-block").text("");
$("#update_emp_modal").modal('hide'); }else{
//后端校验邮件。。。没必要啊
}
}
});
}else{
//邮件有误,不得更新
alert("请正确填写邮件!")
}
},

10.3 Restful API对put请求的使用

首先引入一些能支持Restful API的过滤器,比如通过携前端提交表单时带_method参数可以把表面上为POST请求转换处理成Rest风格的put或delete

    <!-- 浏览器不支持put,delete等method,由该filter将/xxx?_method=delete转换为标准的http delete方法 -->
<filter>
<filter-name>hiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>hiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

因为Tomcat对于前端提交的表单,内部的实现是:将POST请求的报文,把请求体里的参数封装成一个map。

然后request.getParam("")就是从map里获取键值对。而通过查看Tomcat的源码可知:

    /**
* Comma-separated list of HTTP methods that will be parsed according
* to POST-style rules for application/x-www-form-urlencoded request bodies.
*/
protected String parseBodyMethods = "POST";
            //当Tomcat判断不是POST请求,直接返回,不会对请求体进行处理。
//所以request.getParam()根本获取不到参数
if( !getConnector().isParseBodyMethod(getMethod()) ) {
success = true;
return;
}

所以需要一个转换器,以过滤器的方式来增强request对象,让这个转换器替Tomcat识别put请求,并完成put请求报文的封装。让增强后的request调用getParameter()后能获取表单数据。

    <filter>
<filter-name>HttpMethodPutFilter</filter-name>
<filter-class>org.springframework.web.filter.HttpPutFormContentFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HttpMethodPutFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

控制器:

    //获取某一位员工的信息
@RequestMapping(value = "/emps/{empId}",method = RequestMethod.GET)
@ResponseBody
public Msg getEmp(@PathVariable(value = "empId") int empId){
Employee emp = employeeService.getEmp(empId);
return Msg.success().add("emp",emp);
} //修改一位员工的信息
@RequestMapping(value = "/emps/{empId}",method = RequestMethod.PUT)
@ResponseBody
public Msg updateEmp(@PathVariable(value = "empId") int empId, Employee employee){
employeeService.updateEmp(empId,employee);
return Msg.success();
}

11. 删除员工

//    删除一位员工
@RequestMapping(value = "/emps/{empId}",method = RequestMethod.DELETE)
@ResponseBody
public Msg deleteEmp(@PathVariable(value = "empId") int empId){
employeeService.deleteEmp(empId);
return Msg.success();
}
<button type="button"  class="btn btn-sm btn-danger" 	 @click="del_emp(emp.empId,emp.empName)">
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span>
删除
</button>
            //删除单个员工
del_emp:function (empId,empName) {
let self = this; if(confirm("你确认删除【"+empName+"】吗?"))
$.ajax({
url:baseURL+"emps/"+empId,
type:"DELETE",
success:function (result) {
if(result.code==200){
//删除成功
self.getEmps(self.pageInfo.pageNum);
}
}
});
}

12.批量删除

JS批量删除

            //批量删除
del_emp_batch:function () {
let self = this; let checkAll = $("#selectAll").prop("checked");
//可以删除二合一。 多个用-相连接,后端再分离出每个要删除的员工Id
let empIds = new Array(); if(checkAll){
for(let i=0;i<self.emps.length;i++){
console.log(empIds)
empIds.push(self.emps[i].empId);
}
console.log("empIds:"+empIds) let old_empIds = empIds.join(',');
//用-连起来
console.log(typeof old_empIds)
console.log(typeof empIds)
empIds = empIds.join('-'); if(confirm("你确认删除【"+old_empIds+"】吗")){
$.ajax({
url:baseURL+"emps/batch/"+empIds,
type:"DELETE",
success:function (result) {
if(result.code=200){
self.getEmps(self.pageInfo.pageNum+1); //把选中框的状态关掉
$("#selectAll").prop("checked",false);
$("input[name=del_emp]:checked").prop("checked",false);
}
}
});
}
}else{
//拿到所有选中的节点
let del_emps = $("input[name=del_emp]:checked");
$.each(del_emps,function (index,del_emp) {
empIds.push($(del_emp).attr("del-id"));
}) let old_empIds = empIds.join(',') empIds = empIds.join('-'); if(confirm("你确认删除【"+old_empIds+"】吗")){
$.ajax({
url:baseURL+"emps/batch/"+empIds,
type:"DELETE",
success:function (result) {
if(result.code=200){
self.getEmps(self.pageInfo.pageNum); //把选中框的状态关掉
$("#selectAll").prop("checked",false);
$("input[name=del_emp]").prop("checked",false);
}
}
});
}
}
}

全选,非全选:

//全选和全不选
selectAll:function () {
let checkAll = $("#selectAll").prop("checked");
if(checkAll){
//为该页多选框也选中
$("input[name='del_emp']").prop("checked",true);
}else{
$("input[name='del_emp']").prop("checked",false);
}
}, //选中某一个,当该页所有的都被选中,则全选框自动变checked
selectOne:function (empName,empId) {
let checkAll = $("input[name=del_emp]:checked").length == $("input[name=del_emp]").length;
if(checkAll){
$("#selectAll").prop("checked",true);
}else{
$("#selectAll").prop("checked",false);
}
},

JQuery相关语法

<script type="text/javascript">

var arr = new Array(3)
arr[0] = "George"
arr[1] = "John"
arr[2] = "Thomas" document.write(arr.join())
</script>
<head>
<title></title>
<script src="jquery-1.9.0.min.js" type="text/javascript"></script>
<script type="text/javascript">
$(function () {
$('input:hidden').each(function (index, obj) {
alert(obj.name + "..." + obj.value);
});
});
</script>
</head>
<body>
<input type="hidden" value="1" name="a"/>
<input type="hidden" value="2" name="b"/>
<input type="hidden" value="3" name="c"/>
</body>

上面这段代码用到了input集合的索引,有用到了input集合的dom对象,可以通过该对象,拿到其对应的属性如:name,value等;

$.each()方法

  1. 该方法处理一维数组,代码如下:
$.each(["aaa","bbb","ccc"],function(index,value){
alert(i+"..."+value);
});

控制器:

//批量删除按钮触发的
@RequestMapping(value = "/emps/{empIds}",method = RequestMethod.DELETE)
@ResponseBody
public Msg deleteEmp(@PathVariable(value = "empIds") String empIds){
if(empIds.contains("-")){
ArrayList<Integer> ids = new ArrayList<>(); String[] split = empIds.split("-");
for(String empId:split){
int id = Integer.parseInt(empId);
ids.add(id);
employeeService.deleteEmpList(ids);
}
}else{
employeeService.deleteEmp(Integer.parseInt(empIds)); }
return Msg.success();
}

总结:多个id用-连接起来,而不是封装成数组直接传过去,让Springmvc做参数绑定。记住这种技巧。

13.文件上传

总结:

  1. 要在spring配置多媒体解析器

    因为MultipartResolver依赖于Apache的这个jar包

            <!--文件上传的依赖-->
    <dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.3.3</version>
    </dependency>

    spring注册Bean

    <!--配置多媒体解析器-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="UTF-8"></property>
<property name="maxInMemorySize" value="#{1024*1024*1024}"></property>
<property name="maxUploadSize" value="#{1024*1024*5}"></property>
</bean>
@Controller
public class FileController { @RequestMapping(value = "/file",method = RequestMethod.POST)
public String uploadFile(@RequestParam(value = "file") MultipartFile multipartFile, Model model, HttpServletRequest request){ String basePath = "E:/"; if(multipartFile!=null&&!multipartFile.isEmpty()){
//1.获取原始文件名
String originalFilename = multipartFile.getOriginalFilename(); //2.获取前缀
String originalFilenamePrefix = originalFilename.substring(0, originalFilename.lastIndexOf('.')); //3.封装新的文件名 名字+时间戳
String newFileName = originalFilenamePrefix + new Date().getTime()
+ originalFilename.substring(originalFilename.lastIndexOf('.')); //4. 打开文件流
File file = new File(basePath+newFileName); //5. 保存文件
try {
multipartFile.transferTo(file);
model.addAttribute("fileName",originalFilename); } catch (IOException e) {
e.printStackTrace();
System.out.println("上传失败");
}
}
return "upload/uploadSuc";
}

14. 拦截器(登陆认证)

拦截器实现的原理:

  1. 实现HandlerInterceptor接口。编写拦截器
  2. 配置拦截器和HandlerMapping的映射关系
  3. 编写控制器
    <!--拦截器 和HandlerMapping之间的配置 在HandlerMapping找到相应的Handler之前,指定某个拦截器进行拦截-->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="cn.zhanp.ssm_crud.intercepetor.LoginInteceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
public class LoginInteceptor implements HandlerInterceptor {

    /**
* 这个方法 是在 进入Handler方法之前执行的
* 应用:在这里进行身份认证,身份权限认证等等 不过要放行(登陆页面的url)
*/
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
//1. 如果是登陆页面,放行
String requestURI = httpServletRequest.getRequestURI();
if(requestURI.contains("login") || requestURI.contains("toLogin")){
return true;
}else{
HttpSession session = httpServletRequest.getSession();
String username = (String)session.getAttribute("username");
if(username!=null){
return true;
}else{
// 回到登陆页面
httpServletResponse.sendRedirect(httpServletRequest.getContextPath()+"/toLogin");
return false;
}
} } /**
* 这个方法 是在Handler方法之后,返回ModelAndView之前执行的
* 应用: 公用的一些数据模型(ModelAndView)可以在这里设置,比如导航菜单
*/ @Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception { } /**
* 这个方法是在 Handler执行完成后执行
* 应用: 捕捉异常把
*/ @Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { }

SSM框架CRUD小案例的更多相关文章

  1. JavaWeb_(Struts2框架)Ognl小案例查询帖子

    此系列博文基于同一个项目已上传至github 传送门 JavaWeb_(Struts2框架)Struts创建Action的三种方式 传送门 JavaWeb_(Struts2框架)struts.xml核 ...

  2. ssm框架的小总结

    一.mybatis框架 mybatis框架主要就是完成持久层的实现,简化了持久层的开发, 1.首先是配置文件的编写,我们这里就命名为mybatis-config.xml,先配置文件头,然后加载连接数据 ...

  3. angular前端框架简单小案例

    一.angular表达式 <head> <meta charset="UTF-8"> <title>Title</title> &l ...

  4. 一个DRF框架的小案例

    第一步:安装DRF DRF需要以下依赖: Python (2.7, 3.2, 3.3, 3.4, 3.5, 3.6) Django (1.10, 1.11, 2.0) DRF是以Django扩展应用的 ...

  5. 一个ssm综合小案例-商品订单管理----写在前面

    学习了这么久,一直都是零零散散的,没有把知识串联起来综合运用一番 比如拦截器,全局异常处理,json 交互,RESTful 等,这些常见技术必须要掌握 接下来呢,我就打算通过这么一个综合案例把这段时间 ...

  6. ssm框架(Spring Springmvc Mybatis框架)整合及案例增删改查

    三大框架介绍 ssm框架是由Spring springmvc和Mybatis共同组成的框架.Spring和Springmvc都是spring公司开发的,因此他们之间不需要整合.也可以说是无缝整合.my ...

  7. ssm框架的搭建实现CRUD的操作

    最近在开发公司的一个系统,系统的框架是用ssm的框架搭建的,当然和这次写博客的不一样,它拥有很多的配置文件,企业级的开发所需要的配置文件是非常繁琐的,今天记录一下一个简单的SSM框架的搭建和实现一个C ...

  8. react框架实现点击事件计数小案例

    下面将以一个小案例来讲解react的框架的一般应用,重点内容在代码段都有详细的解释,希望对大家有帮助 代码块: 代码块: import React from 'react'; import React ...

  9. springmvc文件上传下载简单实现案例(ssm框架使用)

    springmvc文件上传下载实现起来非常简单,此springmvc上传下载案例适合已经搭建好的ssm框架(spring+springmvc+mybatis)使用,ssm框架项目的搭建我相信你们已经搭 ...

随机推荐

  1. Python学习笔记(一):基础知识

    一.什么是python? python是一种面向对象.解释型的计算机语言,它的特点是语法简洁.优雅.简单易学 二.编译型语言和解释型语言 编译型语言就是把程序编译成计算机语言然后执行,(一次编译到处运 ...

  2. Python爬虫二

    常见的反爬手段和解决思路 1)明确反反爬的主要思路 反反爬的主要思路就是尽可能的去模拟浏览器,浏览器在如何操作,代码中就如何去实现;浏览器先请求了地址url1,保留了cookie在本地,之后请求地址u ...

  3. PHP-redis命令之 列表(lists)

    三.列表(lists) 1.lpush:将所有指定的值插入到存于 key 的列表的头部.如果 key 不存在,那么在进行 push 操作前会创建一个空列表. 如果 key 对应的值不是一个 list ...

  4. BZOJ 2243 染色 树链剖分

    题意: 给出一棵树,每个顶点上有个颜色\(c_i\). 有两种操作: C a b c 将\(a \to b\)的路径所有顶点上的颜色变为c Q a b 查询\(a \to b\)的路径上的颜色段数,连 ...

  5. 常见的Linux目录及其含义

    /                                   系统根目录,通常不会在这里存放文件 . /bin                              二进制目录,存放许多 ...

  6. Apache 根据不同的端口 映射不同的站点

    以前,在本地新建个项目,总是在Apache的htdocs目录下新建个项目目录,今年弄了个别人写好的网站源码,因为该系统的作者假定网站是放在根目录的,放在二级目录下会出错.所以无奈,只能想办法,根据端口 ...

  7. python - 数据驱动测试 - ddt

    # -*- coding:utf-8 -*- ''' @project: jiaxy @author: Jimmy @file: study_ddt.py @ide: PyCharm Communit ...

  8. c4d 宝典部分二

    一.tvart 文字 1.当选择工具 选择面或边时不出现对象坐标时,需要将容错的选项选中 2.当选择两个面右键挤压的时候,如果两个面不分离,需要取消群组并且 拉动箭头的时候需要在空白区域拉动 tvar ...

  9. Spring Boot 必须先说说 Spring 框架!

    现在 Spring Boot 非常火,各种技术文章,各种付费教程,多如牛毛,可能还有些不知道 Spring Boot 的,那它到底是什么呢?有什么用?今天给大家详细介绍一下. Spring Boot ...

  10. 刷题总结——二逼平衡树(bzoj3224线段树套splay)

    题目: Description 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:1.查询k在区间内的排名2.查询区间内排名为k的值3.修改某一位值上的数值4.查询k在 ...