SSM-員工管理專案實戰-CRUD-增刪改查

CHAN發表於2021-03-28

SSM-CRUD

一、專案簡介

主介面演示

截圖2021-03-27 14.15.06

功能點

  • 分頁
  • 資料校驗
  • ajax
  • Rest 風格的 URI

技術點

  • 基礎框架 - ssmSpring + SpringMVC + MyBatis
  • 資料庫 - MySQL
  • 前端框架 - bootstrap (快速簡潔美觀的介面)
  • 專案的依賴管理 - Maven
  • 分頁 - pagehelper
  • 逆向工程 - MyBatis Generator

二、基礎環境搭建

1、建立 Maven 工程 ssm_crud_study

截圖2021-03-27 14.20.09

3、新增 web 框架

(1)右鍵工程,點選 Add Framework Suppor

截圖2021-03-27 14.35.46

(2)選擇 Web Application

截圖2021-03-27 14.36.50

3、在 pom.xml 中引入專案依賴

  • Spring
  • SpringMVC
  • MyBatis
  • 資料庫連線池,驅動包
  • 其他(jstl,servlet-api,junit)

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.chen</groupId>
    <artifactId>ssm-crud</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <!--  pagehelper 分頁外掛      -->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>5.0.0</version>
        </dependency>

        <!--   MBG     -->
        <dependency>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-core</artifactId>
            <version>1.3.5</version>
        </dependency>
        
        <!-- Spring MVC -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>4.3.7.RELEASE</version>
        </dependency>

        <!--  返回 json 字串的支援 -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.8.8</version>
        </dependency>

        <!--  JS303 資料校驗支援      -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>5.4.1.Final</version>
        </dependency>
        
        <!-- Spring Jdbc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>4.3.7.RELEASE</version>
        </dependency>

        <!--  Spring Test      -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>4.3.7.RELEASE</version>
            <scope>test</scope>
        </dependency>

        <!-- 面向切面程式設計 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>4.3.7.RELEASE</version>
        </dependency>

        <!-- Mybatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.2</version>
        </dependency>

        <!-- MyBatis 適配包 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.3.1</version>
        </dependency>

        <!-- 資料庫連線池 -->
        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.2</version>
        </dependency>

        <!-- mysql -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.21</version>
        </dependency>

        <!-- jstl -->
        <dependency>
            <groupId>jstl</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>

        <!-- servlet-api -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.0.1</version>
            <scope>provided</scope>
        </dependency>
        
        <!-- junit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

        <!-- https://mvnrepository.com/artifact/javax.servlet/jsp-api -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.0</version>
            <scope>provided</scope>
        </dependency>
        
    </dependencies>
    
</project>

4、引入 bootstrap 前端框架

(1)前往 bootstrap 官網下載

下載地址: https://v3.bootcss.com,選擇用於生產環境的 Bootstrap

(2)解壓後將資料夾新增到工程中 /static

截圖2021-03-27 20.12.03

5、編寫 ssm 整合的關鍵配置檔案

  • **web.xml,主要用於配置 Filter、Listener、Servlet **

  • springmvc 配置檔案,主要控制網站邏輯的跳轉

  • spring 配置檔案,主要配置與業務邏輯相關的檔案

  • mybatis 配置檔案,主要用於配置 MyBatis

(1)配置 web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!--  1、啟動  Spring 的容器 -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
    </context-param>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- 2、配置 SpringMVC 前端控制器
        預設會載入 WEB-INF 下的 <servletName>-servlet.xml 配置檔案
        即 dispatcherServlet-servlet.xml
    -->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <!-- 3、字元編碼過濾器,一定要放在所有過濾器之前  -->
    <filter>
        <filter-name>characterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceRequestEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>forceResponseEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>characterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!-- 4、使用 Rest 風格的 URI,將頁面普通的 post 請求轉為指定的 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>
    <filter>
        <filter-name>httpPutFormContentFilter</filter-name>
        <filter-class>org.springframework.web.filter.HttpPutFormContentFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>httpPutFormContentFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

在 src/main/resources/ 下加入 applicationContext.xml, 防止 idea 報錯

截圖2021-03-27 15.01.57

(2)配置 springmvc 配置檔案

WEB-INF 下加入 dispatcherServlet-servlet.xml

截圖2021-03-27 15.14.19

src/main/java 建立包結構

截圖2021-03-27 15.17.27

**配置 dispatcherServlet-servlet.xml **

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!--  Spring MVC 的配置檔案,主要控制網站邏輯的跳轉  -->
    <context:component-scan base-package="com.study" 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/views/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

    <!--  兩個標準配置  -->
    <!--  將 Spring MVC 不能處理的請求交給 Tomcat  -->
    <mvc:default-servlet-handler/>
    <!--  Spring MVC 更高階的一些功能,JSR303 檢驗,快捷的 ajax ... 對映動態請求  -->
    <mvc:annotation-driven/>
</beans>

(3) 配置 spring 配置檔案

配置 dbconfig.properties ,配置資料庫連線資訊

src/main/resources 下加入 dbconfig.properties

截圖2021-03-27 15.23.43

dbconfig.properties

jdbc.jdbcUrl=jdbc:mysql://localhost:3306/ssm_crud_study?useUnicode=true&characterEncoding=utf8
jdbc.driverClass=com.mysql.cj.jdbc.Driver
jdbc.user=root
jdbc.password=root

src/main/resources 下建立 mybatis-config.xml、mapper資料夾

截圖2021-03-27 15.27.41

配置 applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"

       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

    <context:component-scan base-package="com.chen">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
    
    <!--  Spring 的配置檔案,主要配置與業務邏輯相關的檔案 -->
    <!--  資料來源、事務控制 ...  -->
    <context:property-placeholder location="classpath:dbconfig.properties"/>
    
    <bean id="pooledDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="jdbcUrl" value="${jdbc.jdbcUrl}"/>
        <property name="driverClass" value="${jdbc.driverClass}"/>
        <property name="user" value="${jdbc.user}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <!--  ================================ 配置和 MyBatis 的整合 =============================== -->
    <bean id="sqlSessionFactoryBean"  class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--    指定 MyBatis 全域性配置檔案的位置    -->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <property name="dataSource" ref="pooledDataSource"/>
        <!--   指定 MyBatis mapper 檔案的位置     -->
        <property name="mapperLocations" value="classpath:mapper/*.xml"/>
    </bean>

    <!--  配置一個執行批量的 sqlSession  -->
    <bean id="sessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
        <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactoryBean"/>
        <constructor-arg name="executorType" value="BATCH"/>
    </bean>

    <!--  配置掃描器,將 MyBatis 介面的實現加入到 ioc 容器中  -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!--   掃描所有 dao 介面的實現,加到 ioc 容器中     -->
        <property name="sqlSessionTemplateBeanName" value="sessionTemplate"/>
        <property name="basePackage" value="com.study"/>
    </bean>

    <!--  事務控制的配置  -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--  控制住資料來源      -->
        <property name="dataSource" ref="pooledDataSource"/>
    </bean>

    <!--  開啟基於註解的事務,使用 xml 配置形式的事務(必要主要的都是使用配置式)  -->
    <aop:config>
        <!--  切入點表示式      -->
        <aop:pointcut id="txPoint" expression="execution(* com.chen.crud.service..*(..))"/>
        <!--  配置事務增強      -->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPoint"/>
    </aop:config>

    <!--  配置事務增強,事務如何切入  -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <!--  所有方法都是事務方法          -->
            <tx:method name="*"/>
            <!--  以 get 開始的所有方法          -->
            <tx:method name="get*" read-only="true"/>
        </tx:attributes>
    </tx:advice>
</beans>

此處目前會報錯,是因為此時 mapper 資料夾目錄下沒有 .xml 檔案,目前可忽略

截圖2021-03-27 15.29.06

(4)配置 mybatis 檔案

mybatis-config.xml

<?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>
        <!-- 開啟駝峰命名自動對映,即從經典資料庫列名 A_COLUMN 對映到經典 Java 屬性名 aColumn -->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>

    <typeAliases>
        <!-- 型別別名可為 Java 型別設定一個縮寫名字
            每一個在包 domain.blog 中的 Java Bean,在沒有註解的情況下,會使用 Bean 的首字母小寫的非限定類名來作為它的別名。 比如 domain.blog.Author 的別名為 author
         -->
        <package name="com.study.crud.bean"/>
    </typeAliases>

    
    <plugins>
        <!-- 分頁外掛 -->
        <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
    </plugins>
</configuration>

6、建立資料庫

CREATE DATABASE ssm_crud_study;

CREATE TABLE `ssm_crud_study`.`tbl_emp` (
  `emp_id` INT NOT NULL AUTO_INCREMENT,
  `emp_name` VARCHAR(255) NOT NULL,
  `gender` CHAR(1) NULL,
  `email` VARCHAR(255) NULL,
  `d_id` INT NULL,
  PRIMARY KEY (`emp_id`));
  
CREATE TABLE `ssm_crud_study`.`tbl_dept` (
  `dept_id` INT NOT NULL,
  `dept_name` VARCHAR(255) NOT NULL,
  PRIMARY KEY (`dept_id`));

ALTER TABLE `ssm_crud_study`.`tbl_emp` 
ADD INDEX `dept_id_idx` (`d_id` ASC) VISIBLE;
;
ALTER TABLE `ssm_crud_study`.`tbl_emp` 
ADD CONSTRAINT `dept_id`
  FOREIGN KEY (`d_id`)
  REFERENCES `ssm_crud_study`.`tbl_dept` (`dept_id`)
  ON DELETE NO ACTION
  ON UPDATE NO ACTION;
截圖2021-03-27 15.48.58

7、使用 mapper 的逆向工程生成 bean、mapper

在工程中新增 mbg.xml

截圖2021-03-27 15.53.16

配置 mbg.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>

    <context id="DB2Tables" targetRuntime="MyBatis3">
        <commentGenerator>
            <!-- 去除生成的 bean、mapper 中的註釋 -->
            <property name="suppressAllComments" value="true" />
        </commentGenerator>

        <!--  配置資料庫連線      -->
        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/ssm_crud_study?useUnicode=true&amp;characterEncoding=utf8"
                        userId="root"
                        password="root">
        </jdbcConnection>

        <javaTypeResolver >
            <property name="forceBigDecimals" value="false" />
        </javaTypeResolver>

        <!--   指定 javaBean 生成的位置     -->
        <javaModelGenerator targetPackage="com.study.crud.bean" targetProject="src/main/java">
            <property name="enableSubPackages" value="true" />
            <property name="trimStrings" value="true" />
        </javaModelGenerator>

        <!--   指定 sql 對映檔案生成的位置     -->
        <sqlMapGenerator targetPackage="mapper"  targetProject="src/main/resources">
            <property name="enableSubPackages" value="true" />
        </sqlMapGenerator>

        <!--   指定 dao 介面生成的位置     -->
        <javaClientGenerator type="XMLMAPPER" targetPackage="com.study.crud.dao"  targetProject="src/main/java">
            <property name="enableSubPackages" value="true" />
        </javaClientGenerator>

        <!--  table 指定每個表的生成策略      -->
        <table tableName="tbl_emp" domainObjectName="Employee">

        </table>
        <table tableName="tbl_dept" domainObjectName="Department"></table>

    </context>
</generatorConfiguration>

src/test/java 中建立 MBGTest 用於生成 bean、mapper

截圖2021-03-27 16.00.24
public class MBGTest {

    public static void main(String[] args) throws Exception {
        List<String> warnings = new ArrayList<String>();
        boolean overwrite = true;
        File configFile = new File("mbg.xml");
        ConfigurationParser cp = new ConfigurationParser(warnings);
        Configuration config = cp.parseConfiguration(configFile);
        DefaultShellCallback callback = new DefaultShellCallback(overwrite);
        MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
        myBatisGenerator.generate(null);
    }
}

執行結果:自動生成 bean、mapper

截圖2021-03-27 16.05.47

8、測試 mapper

在資料庫中新增資料

INSERT INTO `ssm_crud_study`.`tbl_dept` (`dept_id`, `dept_name`) VALUES ('1', '開發部');
INSERT INTO `ssm_crud_study`.`tbl_dept` (`dept_id`, `dept_name`) VALUES ('2', '測試部');

INSERT INTO `ssm_crud_study`.`tbl_emp` (`emp_id`, `emp_name`, `gender`, `email`, `d_id`) VALUES ('1', 'Jerry', 'M', 'jerry@163.com', '1');
截圖2021-03-27 16.09.07

src/test/java 建立 MapperTest

首先測驗能否連線到獲得 mapper 例項

截圖2021-03-27 16.24.57

測試能否獲取資料庫中資訊

截圖2021-03-27 16.28.48

向資料庫中插入 1000 條資料

// 使用 Spring Test
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class MapperTest {

    @Autowired
    DepartmentMapper departmentMapper;

    // 可以執行批量操作的 sqlSession
    @Autowired
    SqlSession sqlSession;

    @Test
    public void testCRUD() {
//        System.out.println(departmentMapper);
//        Department department = departmentMapper.selectByPrimaryKey(1);
//        System.out.println("dept_id=" + department.getDeptId() + ",dept_name=" + department.getDeptName());

        EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
        for (int i = 0; i < 1000; i++) {
            // 使用 UUID 工具類,隨機生成使用者名稱
            String empName = UUID.randomUUID().toString().substring(0, 5);
            // 需要在 Employee 中新增有參構造,!!!新增有參構造後必須新增無參構造 !!!
            mapper.insertSelective(new Employee(null, empName, "M", empName + "@qq.com", 1));
        }
    }
}
截圖2021-03-27 17.23.27

三、CRUD開發

1、index.jsp

流程

  • 訪問 index.jsp 頁面
  • index.jsp 頁面傳送出查詢員工列表請求
  • EmployeeController 來接收請求,查處員工資料
  • 來到 list.jsp 頁面進行展示
  • pageHelper 分頁外掛完成分頁查詢功能

index.jsp 新增標籤 jsp:forward 跳轉到 /emps

截圖2021-03-27 19.09.13

com.study.crud.controller 中新增 EmployeeController,並編寫對映請求

截圖2021-03-27 19.11.16

/WEB-INF/viwes/ 下新增 list.jsp

截圖2021-03-27 19.12.51

啟動伺服器,檢視是否跳轉成功

截圖2021-03-27 19.14.09

我們查詢員工時,希望能夠查詢出部門的資訊,所以需要在 EmployeeMapper 中新增新的查詢方法,並在 Employee 中新增 Department 屬性,和 get、set 方法

截圖2021-03-27 19.20.24 截圖2021-03-27 19.45.13

EmployeeMapper.xml 中新增 SQL

<resultMap id="WithDeptResultMap" type="com.study.crud.bean.Employee">
    <id column="emp_id" jdbcType="INTEGER" property="empId" />
    <result column="emp_name" jdbcType="VARCHAR" property="empName" />
    <result column="gender" jdbcType="CHAR" property="gender" />
    <result column="email" jdbcType="VARCHAR" property="email" />
    <result column="d_id" jdbcType="INTEGER" property="dId" />
    <association property="department" javaType="com.study.crud.bean.Department">
        <id column="dept_id" property="deptId"/>
        <result column="dept_name" property="deptName"/>
    </association>
</resultMap>

<sql id="WithDept_Column_List">
e.emp_id, e.emp_name, e.gender, e.email, e.d_id, d.dept_id, d.dept_name
</sql>

<!--  List<Employee> selectByExampleWithDept(EmployeeExample example);
Employee selectByPrimaryKeyWithDept(Integer empId);-->
<select id="selectByExampleWithDept" resultMap="WithDeptResultMap">
select
<if test="distinct">
  distinct
</if>
<include refid="WithDept_Column_List" />
FROM tbl_emp e
LEFT JOIN tbl_dept d ON e.d_id = d.dept_id
<if test="_parameter != null">
  <include refid="Example_Where_Clause" />
</if>
<if test="orderByClause != null">
  order by ${orderByClause}
</if>
</select>
<select id="selectByPrimaryKeyWithDept" resultMap="WithDeptResultMap">
select
<include refid="WithDept_Column_List" />
FROM tbl_emp e
LEFT JOIN tbl_dept d ON e.d_id = d.dept_id
where emp_id = #{empId,jdbcType=INTEGER}
</select>

EmployeeService 中新增 getAll() 方法

截圖2021-03-27 19.29.22

EmployeeController.getEmps() 中引入分頁外掛

截圖2021-03-27 19.30.59

src/test/ 下新增測試,測試分頁外掛

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(locations = {"classpath:applicationContext.xml", "file:web/WEB-INF/dispatcherServlet-servlet.xml"})
public class MVCTest {

    // 傳入  Spring MVC 的 ioc
    @Autowired
    WebApplicationContext context;

    // 虛擬 mvc 請求,獲取到處理結果
    MockMvc mockMvc;

    @Before
    public void initMockMvc() {
        mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
    }

    @Test
    public void testPage() throws Exception {
        // 模擬請求拿到返回值
        MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get("/emps").param("pn", "5")).andReturn();

        // 請求成功以後,請求域中會有 pageInfo
        MockHttpServletRequest request = result.getRequest();
        PageInfo page = (PageInfo) request.getAttribute("pageInfo");
        System.out.println("當前頁碼:" + page.getPageNum());
        System.out.println("總頁碼:" + page.getPages());
        System.out.println("總記錄數:" + page.getTotal());
        System.out.println("在頁面需要連續顯示的頁碼");
        int[] nums = page.getNavigatepageNums();
        for (int num : nums)
            System.out.print(" " + num);
        System.out.println();

        List<Employee> emps = page.getList();
        for (Employee emp : emps)
            System.out.println("ID:" + emp.getEmpId() + "==>Name:" + emp.getEmpName());
    }
}

測試結果

截圖2021-03-27 19.48.05

2、list.jsp

/static 下加入 jquery

截圖2021-03-27 20.10.57

list.jsp 中引入 bootstrap、jquery

截圖2021-03-27 19.52.28

前往 Bootstrap 官網,檢視文件

截圖2021-03-27 19.59.01

截圖2021-03-27 19.56.34 截圖2021-03-27 19.57.09

根據文件建立 新建 刪除 按鈕

截圖2021-03-27 20.13.27

檢視效果

截圖2021-03-27 20.13.02

給按鈕新增樣式

截圖2021-03-27 20.14.09 截圖2021-03-27 20.16.18

檢視樣式

截圖2021-03-27 20.16.49

根據文件可以寫出表格介面

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>員工列表</title>
    <% pageContext.setAttribute("APP_PATH", request.getContextPath()); %>

    <%--
        不以 / 開始的相對路徑,找資源,以當前資源的路徑為基準,經常容易出問題
        以 / 開始的相對路徑,找資源,以伺服器的路徑為標準 (http://localhost:8080),需要加上專案名
                    http://localhost:8080/crud
    --%>

    <%--  引入 jquery  --%>
    <script type="text/javascript" src="${APP_PATH}/static/js/jquery-1.12.4.min.js"></script>
    <%--  引入樣式  --%>
    <link rel="stylesheet" href="${APP_PATH}/static/bootstrap-3.3.7-dist/css/bootstrap.min.css">
    <script src="${APP_PATH}/static/bootstrap-3.3.7-dist/js/bootstrap.min.js"></script>
</head>
<body>
    <div class="container">
        <!-- 標題 -->
        <!-- class="row" 為柵格系統的行 -->
        <div class="row">
            <!-- col-md-xx 數字代表列數  col-md-offset-xx 數字代表偏移-->
            <div class="col-md-12">
                <h1>SSM-CRUD</h1>
            </div>
        </div>

        <div class="row">
            <div class="col-md-4 col-md-offset-8">
                <button type="button" class="btn-primary">新建</button>
                <button type="button" class="btn-danger">刪除</button>
            </div>
        </div>

        <!-- 顯示錶格資料 -->
        <div class="row">
            <div class="col-md-12">
                <!-- .table-hover 類可以讓 <tbody> 中的每一行對滑鼠懸停狀態作出響應。
                    .table-striped 類可以給 <tbody> 之內的每一行增加斑馬條紋樣式。
                 -->
                <table class="table table-hover table-striped">
                    <tr>
                        <th>#</th>
                        <th>lastName</th>
                        <th>email</th>
                        <th>gender</th>
                        <th>deptName</th>
                        <th>操作</th>
                    </tr>
                    <c:forEach items="${pageInfo.list}" var="emp">
                        <tr>
                            <th>${emp.empId}</th>
                            <th>${emp.empName}</th>
                            <th>${emp.email}</th>
                            <th>${emp.gender == 'M' ? '男' : '女'}</th>
                            <th>${emp.department.deptName}</th>
                            <th>
                                <button class="btn btn-primary btn-sm">
                                    <!-- 鉛筆圖示 -->
                                    <span class="glyphicon glyphicon-pencil" aria-hidden="true"></span>
                                    編輯
                                </button>
                                <button class="btn btn-danger btn-sm">
                                    <!-- 垃圾桶圖示 -->
                                    <span class="glyphicon glyphicon-trash" aria-hidden="true"></span>
                                    刪除
                                </button>
                            </th>
                        </tr>
                    </c:forEach>
                </table>
            </div>
        </div>


    </div>
</body>
</html>

檢視效果

截圖2021-03-27 20.24.58

編寫分頁條

<!-- 顯示分頁資訊 -->
<div class="row">
    <!-- 分頁文字資訊 -->
    <div class="col-md-6">
        當前第 ${pageInfo.pageNum} 頁,共有 ${pageInfo.pages} 頁,總計 ${pageInfo.total} 條記錄
    </div>
    <!-- 分頁條資訊 -->
    <div class="col-md-6">
        <nav aria-label="Page navigation">
            <ul class="pagination">

                <li><a href="${APP_PATH}/emps?pn=1">首頁</a></li>

                <c:if test="${pageInfo.hasPreviousPage}">
                    <li>
                        <a href="${APP_PATH}/emps?pn=${pageInfo.prePage}" aria-label="Previous">
                            <span aria-hidden="true">&laquo;</span>
                        </a>
                    </li>
                </c:if>

                <c:forEach items="${pageInfo.navigatepageNums}" var="page_Num">
                    <c:if test="${page_Num == pageInfo.pageNum}">
                        <li class="active"><a href="#">${page_Num}</a> </li>
                    </c:if>
                    <c:if test="${page_Num != pageInfo.pageNum}">
                        <li><a href="${APP_PATH}/emps?pn=${page_Num}">${page_Num}</a></li>
                    </c:if>
                </c:forEach>

                <li>
                    <a href="#" aria-label="Next">
                        <span aria-hidden="true">&raquo;</span>
                    </a>
                </li>

                <li><a href="${APP_PATH}/emps?pn=${pageInfo.pages}">尾頁</a></li>
            </ul>
        </nav>
    </div>
</div>

檢視效果

截圖2021-03-27 20.33.33

3、ajax

至此已經完成了基本的介面開發,但是目前的設計有著很大的缺陷,每次換頁都需要重新載入整個網頁。

由此引入 AJAX

AJAX = 非同步 JavaScriptXML

AJAX 是一種用於建立快速動態網頁的技術。

通過在後臺與伺服器進行少量資料交換,AJAX 可以使網頁實現非同步更新。這意味著可以在不重新載入整個網頁的情況下,對網頁的某部分進行更新。

傳統的網頁(不使用 AJAX)如果需要更新內容,必需過載整個網頁面。

有很多使用 AJAX 的應用程式案例:新浪微博、Google 地圖、開心網等等。

基於 ajax 查詢員工資料

  1. index.jsp 頁面直接傳送 ajax 請求
  2. 伺服器將查處的資料,以 json 字串的形式返回給瀏覽器
  3. 瀏覽器收到 json 字串,可以使用 jsjson 進行解析,js 通過 dom 增刪改改變頁面
  4. 返回 json。實現客戶端的無關性

(1)去除 index.jsp 中的 <jsp:forward> 標籤,將 list.jsp 中的內容複製到 index.jsp

(2)刪除 index.jsp 頁面中帶有從 EmployeeController.getEmps() 得到的 pageInfo 相關的標籤

得到如下

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
  <title>員工列表</title>
  <% pageContext.setAttribute("APP_PATH", request.getContextPath()); %>

  <%--
      不以 / 開始的相對路徑,找資源,以當前資源的路徑為基準,經常容易出問題
      以 / 開始的相對路徑,找資源,以伺服器的路徑為標準 (http://localhost:8080),需要加上專案名
                  http://localhost:8080/crud
  --%>

  <%--  引入 jquery  --%>
  <script type="text/javascript" src="${APP_PATH}/static/js/jquery-1.12.4.min.js"></script>
  <%--  引入樣式  --%>
  <link rel="stylesheet" href="${APP_PATH}/static/bootstrap-3.3.7-dist/css/bootstrap.min.css">
  <script src="${APP_PATH}/static/bootstrap-3.3.7-dist/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
  <!-- 標題 -->
  <!-- class="row" 為柵格系統的行 -->
  <div class="row">
    <!-- col-md-xx 數字代表列數  col-md-offset-xx 數字代表偏移-->
    <div class="col-md-12">
      <h1>SSM-CRUD</h1>
    </div>
  </div>

  <div class="row">
    <div class="col-md-4 col-md-offset-8">
      <button type="button" class="btn-primary">新建</button>
      <button type="button" class="btn-danger">刪除</button>
    </div>
  </div>

  <!-- 顯示錶格資料 -->
  <div class="row">
    <div class="col-md-12">
      <!-- .table-hover 類可以讓 <tbody> 中的每一行對滑鼠懸停狀態作出響應。
          .table-striped 類可以給 <tbody> 之內的每一行增加斑馬條紋樣式。
       -->
      <table class="table table-hover table-striped">
        <tr>
          <th>#</th>
          <th>lastName</th>
          <th>email</th>
          <th>gender</th>
          <th>deptName</th>
          <th>操作</th>
        </tr>

      </table>
    </div>
  </div>

  <!-- 顯示分頁資訊 -->
  <div class="row">
    <!-- 分頁文字資訊 -->
    <div class="col-md-6">

    </div>
    <!-- 分頁條資訊 -->
    <div class="col-md-6">

    </div>
  </div>
</div>
</body>
</html>

(3) EmployeeController 中新增返回 json 的對映請求

截圖2021-03-27 21.01.31

在瀏覽器中檢視返回的 json 字串

截圖2021-03-27 21.00.53

(4) 編寫訊息類

我們需要將返回的類封裝到一個訊息類中,以便獲取更多的資訊

com.study.crud.bean 中建立 Msg

public class Msg {

    public static final int CODE_SUCCESS = 100;
    public static final int CODE_FAIL = 200;
    
    // 狀態碼
    private int code;
    
    // 提示資訊
    private String msg;
    
    // 返回給瀏覽器的資料
    private Map<String, Object> extend = new HashMap<String, Object>();
    
    public static Msg success() {
        Msg msg = new Msg();
        msg.setCode(CODE_SUCCESS);
        msg.setMsg("處理成功");
        return msg;
    }
    
    public static Msg fail() {
        Msg msg = new Msg();
        msg.setCode(CODE_FAIL);
        msg.setMsg("處理失敗");
        return msg;
    }

    // 向 Msg 中新增資料
    public Msg add(String key, Object value) {
        this.extend.put(key, value);
        return this;
    }
    
    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public Map<String, Object> getExtend() {
        return extend;
    }

    public void setExtend(Map<String, Object> extend) {
        this.extend = extend;
    }
}

改寫 getEmpsWithJson()

截圖2021-03-28 10.20.51

檢視改寫後的對映請求返回的資料

截圖2021-03-28 10.22.37

(4) **編寫 js **

新增 id 值

截圖2021-03-28 14.14.52

新增 js

<script type="text/javascript">
  var lastPage, currentPage, totalRecord;

  // 1、頁面載入完成以後,直接去傳送一個 ajax 請求,要到分頁資料
  $(function () {
    // 去首頁
    to_page(1);
  });

  function to_page(pn) {
    $.ajax({
      url:"${APP_PATH}/emps",
      data:"pn=" + pn,
      type:"get",
      success:function (result) {
        // console.log(result);
        // 1、解析並顯示員工資料
        build_emps_table(result);
        // 2、解析並顯示分頁資訊
        build_page_info(result);
        // 3、解析顯示分頁條資料
        build_page_nav(result);
      }
    });
  }

  function build_emps_table(result) {
    // 清空表格
    $("#emps_table tbody").empty();
    var emps = result.extend.pageInfo.list;
    $.each(emps, function (index, item) {
      var checkBoxTd = $("<td><input type='checkbox' class='check_item'/></td>");
      var empIdTd = $("<td></td>").append(item.empId);
      var empNameTd = $("<td></td>").append(item.empName);
      var gender = item.gender == 'M' ? '男' : '女';
      var genderTd = $("<td></td>").append(gender);
      var emailTd = $("<td></td>").append(item.email);
      var deptNameTd = $("<td></td>").append(item.department.deptName);
      /**
       *  <button class="btn btn-primary btn-sm">
       <span class="glyphicon glyphicon-pencil" aria-hidden="true"></span>
       編輯
       </button>
       */
      var editBtn = $("<button></button>").addClass("btn btn-primary btn-sm edit_btn")
              .append($("<span></span>")).addClass("glyphicon glyphicon-pencil").append("編輯");
      editBtn.attr("edit-id", item.empId);
      var deleteBtn = $("<button></button>").addClass("btn btn-danger btn-sm delete_btn")
              .append($("<span></span>")).addClass("glyphicon glyphicon-trash").append("刪除");
      deleteBtn.attr("del-id", item.empId);
      var btnTd = $("<td></td>").append(editBtn).append(" ").append(deleteBtn);

      // append 方法執行完成以後還是返回原來的元素 所以可以鏈式操作
      $("<tr></tr>").append(checkBoxTd)
              .append(empIdTd)
              .append(empNameTd)
              .append(genderTd)
              .append(emailTd)
              .append(deptNameTd)
              .append(btnTd)
              .appendTo("#emps_table tbody");
    })
  }

  function build_page_info(result) {
    $("#page_info_area").empty();
    var pageInfo = result.extend.pageInfo;
    $("#page_info_area").append("當前 " + pageInfo.pageNum + " 頁,"
            + "總 " + pageInfo.pages + " 頁,"
            + "總 " + pageInfo.total + " 條記錄"
    );
    lastPage = pageInfo.pages;
    totalRecord= pageInfo.total;
    currentPage = pageInfo.pageNum;
  }

  function build_page_nav(result) {
    $("#page_nav_area").empty();
    var pageInfo = result.extend.pageInfo;
    var ul = $("<ul></ul>").addClass("pagination");
    var firstPageLi = $("<li></li>").append($("<a></a>").append("首頁"));
    var prePageLi = $("<li></li>").append($("<a></a>").append("&laquo;"));
    if (pageInfo.hasPreviousPage == false) {
      firstPageLi.addClass("disabled");
      prePageLi.addClass("disabled");
    } else {
      firstPageLi.click(function () {
        to_page(1);
      });
      prePageLi.click(function () {
        to_page(pageInfo.pageNum - 1);
      });
    }
    ul.append(firstPageLi).append(prePageLi);

    $.each(result.extend.pageInfo.navigatepageNums, function (index, item) {
      var numLi = $("<li></li>").append($("<a></a>").append(item));
      if (pageInfo.pageNum == item) {
        numLi.addClass("active");
      }
      numLi.click(function () {
        to_page(item);
      });
      ul.append(numLi);
    });

    var nextPageLi = $("<li></li>").append($("<a></a>").append("&raquo;"));
    var lastPageLi = $("<li></li>").append($("<a></a>").append("尾頁"));
    if (pageInfo.hasNextPage == false) {
      nextPageLi.addClass("disabled");
      lastPageLi.addClass("disabled");
    } else {
      nextPageLi.click(function () {
        to_page(pageInfo.pageNum + 1);
      });
      lastPageLi.click(function () {
        to_page(pageInfo.pages);
      });
    }
    ul.append(nextPageLi).append(lastPageLi);

    var navEle = $("<nav></nav>").append(ul);
    navEle.appendTo("#page_nav_area");
  }
</script>

檢視結果

截圖2021-03-28 14.18.13

4、業務邏輯

(1)新增員工資訊

新增 員工新增的模態框

<!-- 員工新增的模態框 -->
<div class="modal fade" id="empAddModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
  <div class="modal-dialog" role="document">
    <div class="modal-content">
      <div class="modal-header">
        <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
        <h4 class="modal-title" id="myModalLabel">員工新增</h4>
      </div>
      <div class="modal-body">
        <form class="form-horizontal">
          <div class="form-group">
            <label class="col-sm-2 control-label">empName</label>
            <div class="col-sm-10">
              <input type="text" name="empName" class="form-control" id="empName_add_input" placeholder="empName">
              <span class="help-block"></span>
            </div>
          </div>
          <div class="form-group">
            <label class="col-sm-2 control-label">email</label>
            <div class="col-sm-10">
              <input type="text" name="email" class="form-control" id="email_add_input" placeholder="email@atguigu.com">
              <span class="help-block"></span>
            </div>
          </div>
          <div class="form-group">
            <label class="col-sm-2 control-label">gender</label>
            <div class="col-sm-10">
              <label class="radio-inline">
                <input type="radio" name="gender" id="gender1_add_input" value="M" checked="checked"> 男
              </label>
              <label class="radio-inline">
                <input type="radio" name="gender" id="gender2_add_input" value="F"> 女
              </label>
            </div>
          </div>
          <div class="form-group">
            <label class="col-sm-2 control-label">deptName</label>
            <div class="col-sm-4">
              <!-- 部門提交部門id即可 -->
              <select class="form-control" name="dId">
              </select>
            </div>
          </div>
        </form>
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-default" data-dismiss="modal">關閉</button>
        <button type="button" class="btn btn-primary" id="emp_save_btn">儲存</button>
      </div>
    </div>
  </div>
</div>

新建按鈕處新增 id

截圖2021-03-28 14.29.09

編寫 js

//清空表單樣式及內容
  function reset_form(ele) {
    $(ele)[0].reset();
    //清空表單樣式
    $(ele).find("*").removeClass("has-error has-success");
    $(ele).find(".help-block").text("");
  }

  function getDepts(ele) {
    // 清空之前下拉選單的值
    $(ele).empty();
    $.ajax({
      url: "${APP_PATH}/depts",
      type: "get",
      success:function (result) {
        //{"code":100,"msg":"處理成功!",
        //"extend":{"depts":[{"deptId":1,"deptName":"開發部"},{"deptId":2,"deptName":"測試部"}]}}
        //console.log(result);
        //顯示部門資訊在下拉選單中
        //$("#empAddModal select").append("")
        $.each(result.extend.depts,function(){
          var optionEle = $("<option></option>").append(this.deptName).attr("value",this.deptId);
          optionEle.appendTo(ele);
        });
      }
    });
  }

  // 點選新增按鈕彈出模態框。
  $("#emp_add_modal_btn").click(function () {
    //清除表單資料(表單完整重置(表單的資料,表單的樣式))
    reset_form("#empAddModal form");
    //s$("")[0].reset();
    //傳送ajax請求,查出部門資訊,顯示在下拉選單中
    getDepts("#empAddModal select");
    //彈出模態框
    $("#empAddModal").modal({
      backdrop:"static"
    });
  });

檢視 新增模態框

截圖2021-03-28 14.30.54

建立 DepartmentController 對映 getDepts 請求

@Controller
public class DepartmentController {

    @Autowired
    DepartmentService departmentService;

    @RequestMapping("/depts")
    @ResponseBody
    public Msg getDeptsWithJson() {
        List<Department> depts = departmentService.getDepts();
        return Msg.success().add("depts", depts);
    }
}

@Service
public class DepartmentService {

    @Autowired
    DepartmentMapper departmentMapper;

    public List<Department> getDepts() {
        return departmentMapper.selectByExample(null);
    }
}

檢視是否顯示部門資訊

截圖2021-03-28 16.01.23

(2)資料校驗

**js校驗 **

// 檢驗表單資料
  function validate_add_form() {
    var empName = $("#empName_add_input").val();
    var regName = /(^[a-zA-Z0-9]{6,16}$)|(^[\u2E80-\u9FFF]{2,5})/;
    if (!regName.test(empName)) {
      show_validate_msg("#empName_add_input", "error", "使用者名稱可以是 2-5 位中文或者 6-16 位英文和數字的組合");
      return false;
    } else {
      show_validate_msg("#empName_add_input", "success", "");
    }

    var email = $("#email_add_input").val();
    var regEmail = /^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/;
    if (!regEmail.test(email)) {
      show_validate_msg("#email_add_input", "error", "郵箱格式不正確");
      return false;
    } else {
      show_validate_msg("#email_add_input", "success", "");
    }
    return true;
  }

  // 顯示校驗結果的提示資訊
  function show_validate_msg(ele, status, msg) {
    $(ele).parent().removeClass("has-success has-error");
    $(ele).next("span").text("");
    if ("success" == status) {
      $(ele).parent().addClass("has-success");
      $(ele).next("span").text(msg);
    } else {
      $(ele).parent().addClass("has-error");
      $(ele).next("span").text(msg);
    }
  }

  // 校驗使用者名稱是否可用
  $("#empName_add_input").change(function () {
    var empName = this.value;
    $.ajax({
      url:"${APP_PATH}/checkuser",
      data:"empName="+empName,
      type:"post",
      success:function (result) {
        if (result.code == 100) {
          show_validate_msg("#empName_add_input", "success", "使用者名稱可用");
          $("emp_save_btn").attr("ajax-va", "success");
        } else {
          show_validate_msg("#empName_add_input", "error", result.extend.va_msg);
          $("emp_save_btn").attr("ajax-va", "error");
        }
      }
    });
  });

EmployeeController 中新增檢驗使用者名稱對映請求

// EmployeeController
@RequestMapping("/checkuser")
@ResponseBody
public Msg checkuser(@RequestParam("empName") 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 b = employeeService.checkUser(empName);
    if (b) {
        return Msg.success();
    } else {
        return Msg.fail().add("va_msg", "使用者名稱已存在");
    }
}

EmployeeService 中新增檢驗方法

public boolean checkUser(String empName) {
    EmployeeExample example = new EmployeeExample();
    EmployeeExample.Criteria criteria = example.createCriteria();
    criteria.andEmpNameEqualTo(empName);
    long count = employeeMapper.countByExample(example);
    return count == 0;
}

檢視校驗結果

截圖2021-03-28 16.10.20 截圖2021-03-28 16.10.51

(3) 儲存員工

新增 js

// 點選儲存,儲存員工
  $("#emp_save_btn").click(function () {
    // 1、模態框中填寫的資料提交給伺服器進行儲存
    // 1、先對要提交給伺服器的資料進行校驗
    if (!validate_add_form()) {
      return false;
    }
    // 1、判斷之前的 ajax 使用者名稱校驗是否成功
    if ($(this).attr("ajax-va") == "error") {
      return false;
    }

    // 2、傳送 ajax 請求儲存員工
    $.ajax({
      url:"${APP_PATH}/emp",
      type:"post",
      data:$("#empAddModal form").serialize(),
      success:function(result) {
        if (result.code == 100) {
          // 員工儲存成功
          // 1、關閉模態框
          $("#empAddModal").modal("hide");
          // 2、來到最後一頁,顯示剛才儲存的資料
          if (totalRecord % 5 == 0) {
            lastPage++;
            currentPage = lastPage;
          }
          totalRecord++;
          to_page(lastPage);
        } else {
          // 顯示失敗資訊
          if (undefined != result.extend.errorFields.email) {
            // 顯示郵箱錯誤資訊
            show_validate_msg("#email_add_input", "error", result.extend.errorFields.email);
          }
          if (undefined != result.extend.errorFields.empName) {
            // 顯示員工名字的錯誤資訊
            show_validate_msg("#empName_add_input", "error", result.extend.errorFields.empName);
          }
        }
      }
    });
  });

Employee 中使用JSR303 資料校驗

截圖2021-03-28 16.17.42

EmployeeController 中新增儲存員工的對映請求

@RequestMapping(value = "/emp", method = RequestMethod.POST)
@ResponseBody
public Msg saveEmp(@Valid Employee employee, BindingResult result) {
    if (result.hasErrors()) {
        Map<String, Object> map = new HashMap<String, Object>();
        List<FieldError> errors = result.getFieldErrors();
        for (FieldError fieldError : errors) {
            System.out.println("錯誤的欄位名:" + fieldError.getField());
            System.out.println("錯誤資訊:" + fieldError.getDefaultMessage());
            map.put(fieldError.getField(), fieldError.getDefaultMessage());
        }
        return Msg.fail().add("errorFields", map);
    } else {
        employeeService.saveEmp(employee);
        return Msg.success();
    }
}

EmployeeService 中新增儲存員工的方法

public void saveEmp(Employee employee) {
    employeeMapper.insertSelective(employee);
}
截圖2021-03-28 16.20.14 截圖2021-03-28 16.20.43

(4) 修改員工資訊

新增員工修改模態框

<!-- 員工修改的模態框 -->
<div class="modal fade" id="empUpdateModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
  <div class="modal-dialog" role="document">
    <div class="modal-content">
      <div class="modal-header">
        <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
        <h4 class="modal-title">員工修改</h4>
      </div>
      <div class="modal-body">
        <form class="form-horizontal">
          <div class="form-group">
            <label class="col-sm-2 control-label">empName</label>
            <div class="col-sm-10">
              <p class="form-control-static" id="empName_update_static"></p>
            </div>
          </div>
          <div class="form-group">
            <label class="col-sm-2 control-label">email</label>
            <div class="col-sm-10">
              <input type="text" name="email" class="form-control" id="email_update_input" placeholder="email@atguigu.com">
              <span class="help-block"></span>
            </div>
          </div>
          <div class="form-group">
            <label class="col-sm-2 control-label">gender</label>
            <div class="col-sm-10">
              <label class="radio-inline">
                <input type="radio" name="gender" id="gender1_update_input" value="M" checked="checked"> 男
              </label>
              <label class="radio-inline">
                <input type="radio" name="gender" id="gender2_update_input" value="F"> 女
              </label>
            </div>
          </div>
          <div class="form-group">
            <label class="col-sm-2 control-label">deptName</label>
            <div class="col-sm-4">
              <!-- 部門提交部門id即可 -->
              <select class="form-control" name="dId">
              </select>
            </div>
          </div>
        </form>
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-default" data-dismiss="modal">關閉</button>
        <button type="button" class="btn btn-primary" id="emp_update_btn">更新</button>
      </div>
    </div>
  </div>
</div>

新增 js

// 1、如果在編輯和刪除按鈕建立之前繫結 click 是繫結不上的
  // 1)可以在建立按鈕的時候繫結
  // 2)繫結點選 .live()
  $(document).on("click", ".edit_btn", function () {
    // 1、查處部門資訊,並顯示部門列表
    getDepts("#empUpdateModal select");
    // 2、查處員工資訊,顯示員工資訊
    getEmp($(this).attr("edit-id"));

    // 3、把員工的 id 傳遞給模態框的更新按鈕
    $("#emp_update_btn").attr("edit-id", $(this).attr("edit-id"));
    $("#empUpdateModal").modal({
      backdrop: "static"
    });
  });

  function getEmp(id) {
    $.ajax({
      url:"${APP_PATH}/emp/" + id,
      type:"get",
      success:function (result) {
        var empData = result.extend.emp;
        $("#empName_update_static").text(empData.empName);
        $("#email_update_input").val(empData.email);
        $("#empUpdateModal input[name=gender]").val([empData.gender]);
        $("#empUpdateModal select").val([empData.dId]);
      }
    });
  }

  // 點選更新,更新員工資訊
  $("#emp_update_btn").click(function () {
    // 驗證郵箱是否合法
    // 1、校驗郵箱資訊
    var email = $("#email_update_input").val();
    var regEmail = /^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/;
    if (!regEmail.test(email)) {
      show_validate_msg("#email_update_input", "error", "郵箱格式不正確");
      return false;
    } else {
      show_validate_msg("#email_update_input", "success", "");
    }

    // 2、傳送 ajax 請求儲存更新的員工資料
    $.ajax({
      url:"${APP_PATH}/emp/" + $(this).attr("edit-id"),
      type:"put",
      data:$("#empUpdateModal form").serialize(),
      success:function (result) {
        $("#empUpdateModal").modal("hide");
        to_page(currentPage);
      }
    });
  });

EmployeeController 中新增按 id 獲取員工的對映請求 和 更新員工的請求

@RequestMapping(value = "/emp/{id}", method = RequestMethod.GET)
@ResponseBody
public Msg getEmp(@PathVariable("id") Integer id) {
    Employee employee = employeeService.getEmp(id);
    return Msg.success().add("emp", employee);
}

@RequestMapping(value = "/emp/{empId}", method = RequestMethod.PUT)
@ResponseBody
public Msg updateEmp(Employee employee, HttpServletRequest request) {
    employeeService.updateEmp(employee);
    return Msg.success();
}

EmployeeService 中新增 getEmp 方法和 update 方法

public Employee getEmp(Integer id) {
    return employeeMapper.selectByPrimaryKeyWithDept(id);
}

public void updateEmp(Employee employee) {
    employeeMapper.updateByPrimaryKeySelective(employee);
}

檢視效果

截圖2021-03-28 16.38.29截圖2021-03-28 16.39.26

截圖2021-03-28 16.39.45

(5) 新增全選功能

新增全選按鈕

截圖2021-03-28 16.43.39

新增 js

  // 完成全選、全不選功能
  $("#check_all").click(function () {
    $(".check_item").prop("checked", $(this).prop("checked"));
  });

  $(document).on("click", ".check_item", function () {
    var flag = $(".check_item:checked").length == $(".check_item").length;
    $("#check_all").prop("checked", flag);
  });

新增刪除功能

給刪除按鍵新增 id

截圖2021-03-28 16.53.57

新增 js

  // 單個刪除
  $(document).on("click", ".delete_btn", function () {
    var empName = $(this).parents("tr").find("td:eq(2)").text();
    var empId = $(this).attr("del-id");
    // 彈出是否確認刪除對話方塊
    if (confirm("確認刪除【" + empName + "】嗎?")) {
      $.ajax({
        url:"${APP_PATH}/emp/" + empId,
        type:"delete",
        success:function (result) {
          alert(result.msg);
          to_page(1);
        }
      });
    }
  });

  // 點選全部刪除、就批量刪除
  $("#emp_delete_all_btn").click(function () {
    var empNames = "";
    var del_ids = "";
    $.each($(".check_item:checked"), function () {
      empNames += $(this).parents("tr").find("td:eq(2)").text() + ",";
      del_ids += $(this).parents("tr").find("td:eq(1)").text() + "-";
    });
    empNames = empNames.substring(0, empNames.length - 1);
    del_ids = del_ids.substring(0, del_ids.length - 1);
    if (confirm("確認刪除【" + empNames + "】嗎?")) {
      $.ajax({
        url:"${APP_PATH}/emp/" + del_ids,
        type:"delete",
        success:function (result) {
          alert(result.msg);
          to_page(1);
        }
      });
    }
  });

EmployeeController 中新增刪除方法

@RequestMapping(value = "/emp/{ids}", method = RequestMethod.DELETE)
@ResponseBody
public Msg deleteEmp(@PathVariable("ids") String ids) {
    // 批量刪除
    if (ids.contains("-")) {
        List<Integer> del_ids = new ArrayList<Integer>();
        String[] str_ids = ids.split("-");
        for (String str : str_ids) {
            del_ids.add(Integer.parseInt(str));
        }
        employeeService.deleteBatch(del_ids);
    } else {
        Integer id = Integer.parseInt(ids);
        employeeService.deleteEmp(id);
    }
    return Msg.success();
}

EmployeeService中新增單個刪除和多個刪除的方法

public void deleteBatch(List<Integer> del_ids) {
    EmployeeExample example = new EmployeeExample();
    EmployeeExample.Criteria criteria = example.createCriteria();
    criteria.andEmpIdIn(del_ids);
    employeeMapper.deleteByExample(example);
}

public void deleteEmp(Integer id) {
    employeeMapper.deleteByPrimaryKey(id);
}

檢視效果

截圖2021-03-28 16.49.42 截圖2021-03-28 16.54.52 截圖2021-03-28 16.55.11

四、總結

image-20210328170831074

相關文章