CRUD demo of SSM integration

1, Create a maven project

2, Introduce the jar package that the project depends on [new knowledge mybatis reverse engineering, spring unit test]

<?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>org.example</groupId>
    <artifactId>SSM-CRUDdemo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <dependencies>
        <!--        - - - - - - -   - - - - - - - - - - Spring relevant jar package- - - -  - - - - - - - - - - - - - - - - - - - - - - -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.3.5</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.3.5</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.4</version>
        </dependency>
        <!--        - - - - - - -   - - - - - - - - - - Mybatis relevant jar package- - - -  - - - - - - - - - - - - - - - - - - - - - - -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.6</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.2</version>
        </dependency>
        <!--        - - - - - - -   - - - - - - - - - - Database related jar package- - - -  - - - - - - - - - - - - - - - - - - - - - - -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <!--        c3p0 Connection pool-->
        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.5.2</version>
        </dependency>
        <!--        druid Connection pool-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.3</version>
        </dependency>
        <!--        - - - - - - -   - - - - - - - - - - Servlet-JSP relevant jar package- - - -  - - - - - - - - - - - - - - - - - - - - - - -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>javax.servlet.jsp-api</artifactId>
            <version>2.3.3</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>
        <!--        - - - - - - -   - - - - - - - - - - Other related jar package- - - -  - - - - - - - - - - - - - - - - - - - - - - -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
            <scope>provided</scope>
        </dependency>
        <!--    json-->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.12.1</version>
        </dependency>
        <!-- mybatis Reverse engineering dependency -->
        <dependency>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-core</artifactId>
            <version>1.3.7</version>
        </dependency>
<!--        spring Unit test module-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.3.5</version>
            <scope>test</scope>
        </dependency>
<!--        Paging plug-in-->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>5.2.0</version>
        </dependency>
<!--        JSR303 check-->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>6.0.13.Final</version>
        </dependency>
    </dependencies>
    <!--    stay build Medium configuration resources , To prevent the failure of resource export-->
    <build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
            </resource>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
            </resource>
        </resources>
    </build>

</project>

3, Build database environment, establish basic structure and configuration framework!

1. Build database environment

2. Establish the basic structure and configuration framework!

4, Consolidation profile

4.1. Integrate configuration files before reverse engineering without mybatis

4.2. Integrate configuration files after reverse engineering with mybatis

mybatis reverse engineering

A major feature of mybatis is that programmers need to write SQL by themselves. If there are too many tables, it will inevitably be very troublesome. Therefore, mybatis officially provides a reverse engineering, which can automatically generate the code required for mybatis execution for a single table (including pojo class, mapper.xml mapping file and Mapper interface). Generally, in practical development, the common way of reverse engineering is to generate code through the table of the database.

4.2.1. Create the configuration file generatorconfig. Required for reverse engineering of mybatis 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">
        <!-- Remove generated annotations -->
        <commentGenerator>
            <property name="suppressAllComments" value="true"/>
        </commentGenerator>
        <!-- Database connection configuration -->
        <!-- be careful xml Not supported in&,use&amp;replace -->
        <jdbcConnection
                driverClass="com.mysql.jdbc.Driver"
                connectionURL="jdbc:mysql://localhost:3306/ssm_crud?useUnicode=true&amp;characterEncoding=utf8&amp;useSSL=false"
                userId="root"
                password="xbh123">
        </jdbcConnection>

        <!-- handle NUMERIC and DECIMAL Type of policy -->
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false" />
        </javaTypeResolver>

        <!-- to configure pojo Generated location -->
        <javaModelGenerator targetPackage="com.xbh.pojo"  targetProject=".\src\main\java">
            <property name="enableSubPackages" value="true" />
            <property name="trimStrings" value="true" />
        </javaModelGenerator>

        <!-- to configure sql Generation location of mapping file -->
        <sqlMapGenerator targetPackage="com.xbh.dao" targetProject=".\src\main\java">
            <property name="enableSubPackages" value="true" />
            <property name="trimStrings" value="true" />
        </sqlMapGenerator>

        <!-- to configure dao Generation location of interface -->
        <javaClientGenerator type="XMLMAPPER" targetPackage="com.xbh.dao" targetProject=".\src\main\java">
            <property name="enableSubPackages" value="true" />
            <property name="trimStrings" value="true" />
        </javaClientGenerator>

        <!-- Specifies the data table on which the reverse is based -->
        <table tableName="tbl_emp" domainObjectName="Employee"></table>
        <table tableName="tbl_dept" domainObjectName="Department"></table>

    </context>
</generatorConfiguration>

Note: in this configuration file, the position of each element label is required. If the position is wrong, an error will be reported

4.2.2 generating program

public class GBKtest {
        public static void main(String[] args) throws Exception {
            List<String> warnings = new ArrayList<String>();
            boolean overwrite = true;
            File configFile = new File("D:\\workspace_idea\\SSM-CRUDdemo\\src\\main\\resources\\generatorConfig.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);
            System.out.println("Generated successfully!");
        }
}

4.2.3. Generate results

4.2.4 advantages and disadvantages of reverse engineering

  • Advantages: it helps us automatically generate Java code and greatly speeds up our development efficiency.
  • Disadvantages: the generated files are too redundant and there is too much unnecessary code. In particular, there are too many configuration contents in the sql mapping file. For the dao layer, the methods provided are relatively limited and need to be extended by itself.

4.2.4. Spring integrates relevant configuration files of Mybatis; spring-dao.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd">
    <!--    1,Associated database profile-->
    <context:property-placeholder location="classpath:database.properties"/>
    <!--    3,Database connection pool-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!-- Configure connection pool properties -->
        <property name="driverClass" value="com.mysql.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/ssm_crud?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf8"/>
        <property name="user" value="root"/>
        <property name="password" value="xbh123"/>
        <!-- c3p0 Private properties of connection pool -->
        <property name="maxPoolSize" value="30"/>
        <property name="minPoolSize" value="10"/>
        <!-- Not automatically after closing the connection commit -->
        <property name="autoCommitOnClose" value="false"/>
        <!-- Get connection timeout -->
        <property name="checkoutTimeout" value="10000"/>
        <!-- Number of retries when getting connection failed -->
        <property name="acquireRetryAttempts" value="2"/>
    </bean>
    <!--    3,SqlSessionFactory-->
    <!-- 3.to configure SqlSessionFactory object -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- Inject database connection pool -->
        <property name="dataSource" ref="dataSource"/>
        <!-- to configure MyBatis Global profile:mybatis-config.xml -->
        <property name="configLocation" value="classpath:Mybatis-config.xml"/>

    </bean>
<!--    Configure a batch execution sqlSession-->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
        <constructor-arg name="executorType" value="BATCH"/>
    </bean>

    <!-- 4.Configure scan Dao Interface package, dynamic implementation Dao Interface injection into spring In container -->
    <!--Explanation: https://www.cnblogs.com/jpfss/p/7799806.html-->
<!--    Mybatis MapperScannerConfigurer Automatic scanning will Mapper Interface generation agent injection Spring,
here mybatis-config.xml Inside<mappers><mappers/>If no configuration is required, it will be automatically scanned spring in-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!-- injection sqlSessionFactory -->
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
        <!-- Given the need to scan Dao Interface package -->
        <property name="basePackage" value="com.xbh.dao"/>
    </bean>
</beans>

4.2.5 Spring integration service layer -- Spring service 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
       https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       https://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx.xsd">

    <context:annotation-config/>
    <!--    1,scanning service Package under-->
    <context:component-scan base-package="com.xbh.service"/>
    <!--    2,Inject all our business classes into spring,It can be implemented through configuration or annotation-->
<!--    <bean id="bookServiceImpl" class="com.xbh.service.BookServiceImpl">-->
<!--        <property name="bookMapper" ref="bookMapper"/>-->
<!--    </bean>-->
    <!--    3,Configure transaction manager-->

    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- Inject database connection pool -->
        <property name="dataSource" ref="dataSource" />
    </bean>
    <!--    4,aop Transaction support-->
    <!--    combination aop Implement transaction weaving-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager" >
        <!--        Which methods are configured with transactions-->
        <tx:attributes>
            <tx:method name="*" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>
    <!--    Configure transaction entry-->
    <aop:config>
        <aop:pointcut id="txPointCut" expression="execution(* com.xbh.dao.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
    </aop:config>
</beans>

4.2.6. Add web support and write 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">

    <!--DispatcherServlet-->
    <servlet>
        <servlet-name>DispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <!--Be careful:What we load here is the total configuration file, which was damaged here before!-->
            <param-value>classpath:applicationContext.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>
    <!--encodingFilter-->
    <filter>
        <filter-name>encodingFilter</filter-name>
        <filter-class>
            org.springframework.web.filter.CharacterEncodingFilter
        </filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    
<!--    use Rest Stylized URL-->
    <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>

    <!--Session Expiration time-->
    <session-config>
        <session-timeout>15</session-timeout>
    </session-config>

</web-app>

4.2.7. Spring integrates the controller layer and writes spring MVC 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:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/mvc
        https://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">


    <!-- to configure SpringMVC -->
    <!-- 1.open SpringMVC Annotation driven -->
    <mvc:annotation-driven />
    <!-- 2.Static resource default servlet to configure-->
    <mvc:default-servlet-handler/>

    <!-- 3.to configure jsp display ViewResolver view resolver  -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
        <property name="prefix" value="/WEB-INF/" />
        <property name="suffix" value=".jsp" />
    </bean>
    <!-- 4.scanning web dependent bean -->
    <context:component-scan base-package="com.xbh.controller" />


</beans>

5, Test dao layer

5.1. Using traditional unit test

     ApplicationContext Context = new ClassPathXmlApplicationContext("applicationContext.xml");
     DepartmentMapper bean = Context.getBean(DepartmentMapper.class);

5.1. Using spring unit test, we can automatically inject the components we need

1. Import the jar package required by SpringTest

<!--        spring Unit test module-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.3.5</version>
            <scope>test</scope>
        </dependency>

2. Using annotations
@ContextConfiguration specifies the location of the spring configuration file. locations = {"classpath:applicationContext.xml"}
@RunWith specifies which unit test to use. Spring junit 4classrunner defined in junit class

//Specify spring tester
@RunWith(SpringJUnit4ClassRunner.class)
//Using spring testing
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class MapperTest {
    @Autowired
    DepartmentMapper departmentMapper;
    @Autowired
    EmployeeMapper employeeMapper;
    @Autowired
    SqlSession SqlSession;
    @Test
    public void testCrud(){

        departmentMapper.insertSelective(new Department("Development Department"));
        departmentMapper.insertSelective(new Department("Testing department"));
        employeeMapper.insertSelective(new Employee(null,"Wang Qian","female","34009750@qq.com",1));
        employeeMapper.insertSelective(new Employee(null,"Xu Baohua","male","34009750@qq.com",2));
//        Batch operation
        EmployeeMapper mapper = SqlSession.getMapper(EmployeeMapper.class);
        for(int i=0;i<1000;i++){
            String name=UUID.randomUUID().toString().substring(0,5)+i;
            mapper.insertSelective(new Employee(null,name,"male",name+"@qq.com",1));
        }
    }
}

Before using batch operations, you need to use spring Dao Configure a batch sqlSession that can be executed in XML

<!--    Configure a batch execution sqlSession-->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
        <constructor-arg name="executorType" value="BATCH"/>
    </bean>

results of enforcement

The batch operation was also successful

6, Query business

6.1. Query all books, and use PageHepler paging plug-in for paging query (use modal to return data)

  • 1. Introduce PageHepler paging plug-in jar package
<!--        Paging plug-in-->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>5.2.0</version>
        </dependency>
  • 2. Registering plug-ins in Mybatis
    <plugins>
        <plugin interceptor="com.github.pagehelper.PageInterceptor">
<!--            set a property reasonable by true When, pageNum<=0 The default is the first page-->
            <property name="reasonable" value="true"/>
        </plugin>
    </plugins>
  • 3. You only need to call before querying

  • controller

    @Autowired
    EmployeeService employeeService;
    //Get all employees
    @RequestMapping("/emps")
    public String getEmps(@RequestParam(value="pn",defaultValue = "1")Integer pn, Model model){
        //Introduce pagehelper paging plug-in
        PageHelper.startPage(pn,5);

        List<Employee> employeeList=employeeService.getAll();
        //Wrap the query results with pageInfo
        PageInfo page = new PageInfo(employeeList,5);
        model.addAttribute("pageInfo",page);
        return "list";
    }

PageHelper.startPage(page, rows);

PageInfo.class is a class in the plug-in, which is very convenient to call. Paging improves performance again:

    //Current page
    private int pageNum;
    //Number of pages
    private int pageSize;
    //Number of current pages
    private int size;
 
    //Since startRow and endRow are not commonly used, here is a specific usage
    //You can "display a total of size data from startRow to endRow" on the page
 
    //The row number of the first element of the current page in the database
    private int startRow;
    //The row number of the last element of the current page in the database
    private int endRow;
 
    //Total records
    private long total;
    //PageCount 
    private int pages;
    //Result set
    private List<T> list;
 
    //next page before
    private int prePage;
    //next page
    private int nextPage;
    //Is it the first page
    private boolean isFirstPage;
    //Is it the last page
    private boolean isLastPage;
    //Is there a previous page
    private boolean hasPreviousPage;
    //Is there a next page
    private boolean hasNextPage;
    //Navigation page number
    private int navigatePages;
    //All navigation page numbers
    private int[] navigatepageNums;
    //First page on the navigation bar
    private int navigateFirstPage;
    //Last page on the navigation bar
    private int navigateLastPage;
 
    public PageInfo() {
        this.isFirstPage = false;
        this.isLastPage = false;
        this.hasPreviousPage = false;
        this.hasNextPage = false;
    }            
  • 4. Testing
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class MvcTest {
    MockMvc mockMvc;
    @Autowired
    WebApplicationContext context;
    @Before
    public void initMokcMvc(){

        mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
    }
    @Test
    public void testPage() throws Exception {
//        Get the return value of the simulation request
        MvcResult pn = mockMvc.perform(MockMvcRequestBuilders.get("/emps").param("pn", "1")).andReturn();
        //After the request is successful, there will be pageInfo in the request domain
        MockHttpServletRequest request = pn.getRequest();
        PageInfo pageInfo = (PageInfo)request.getAttribute("pageInfo");
        System.out.println("Current page number"+pageInfo.getPageNum());
        System.out.println("Total page number"+pageInfo.getPages());
        System.out.println("Total records"+pageInfo.getTotal());
        System.out.println("The number of pages that need to be displayed continuously on the page");
        int[] navigateFirstPage = pageInfo.getNavigatepageNums();
        for (int i : navigateFirstPage) {
            System.out.print(i);
        }

    }
}
  • test result

6.2. Write a list JSP page data (return data through model)

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>Title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <!-- introduce Bootstrap -->
    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
    <!-- jQuery (Bootstrap of JavaScript The plug-in needs to be imported jQuery) -->
    <script src="https://code.jquery.com/jquery.js"></script>
    <!-- Include all compiled plug-ins -->
    <script src="js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
    <div class="row">
        <div class="col-md-12">
            <h1>SSM-CRUD</h1>
        </div>
    </div>
    <div class="row">
        <div class="col-md-4 col-md-offset-12">
            <button class="btn btn-success" >newly added</button>
            <button class="btn btn-danger">delete</button>
        </div>
    </div>
    <div class="row">
        <div class="col-md-12">
            <table class="table table-hover table-striped">
                <tr>
                    <th>Employee number</th>
                    <th>Employee name</th>
                    <th>Employee gender</th>
                    <th>mailbox</th>
                    <th>Employee Department</th>
                    <th>Operation button</th>
                </tr>
                <c:forEach items="${pageInfo.list}" var="emp">
                    <tr>
                        <th>${emp.empId}</th>
                        <th>${emp.empName}</th>
                        <th>${emp.gender}</th>
                        <th>${emp.email}</th>
                        <th>${emp.department.deptName}</th>
                        <th>
                            <button class="btn btn-success btn-sm" >
                                <span class="glyphicon glyphicon-pencil " aria-hidden="true"></span>
                                edit
                            </button>
                            <button class="btn btn-danger btn-sm">
                                <span class="glyphicon glyphicon-trash" aria-hidden="true"></span>
                                delete
                            </button>
                        </th>
                    </tr>
                </c:forEach>

            </table>
        </div>
    </div>
    <div class="row">
        <div class="col-md-6">
            Current page number[ ${pageInfo.pageNum}],Altogether[ ${pageInfo.pages}]Page, total[ ${pageInfo.total}]Records
        </div>
        <div class="col-md-6">
            <ul class="pagination">
                <li ><a href="${pageContext.request.contextPath}/emps?pn=1">home page</a></li>
                <c:if test="${pageInfo.hasPreviousPage}">
                    <li><a href="${pageContext.request.contextPath}/emps?pn=${pageInfo.pageNum-1}">&laquo;</a></li>
                </c:if>
                <c:forEach items="${pageInfo.navigatepageNums}" var="pageNum">
                    <c:if test="${pageNum==pageInfo.pageNum}">
                        <li class="active"><a href="#">${pageNum}</a></li>
                    </c:if>
                    <c:if test="${pageNum!=pageInfo.pageNum}">
                        <li><a href="${pageContext.request.contextPath}/emps?pn=${pageNum}">${pageNum}</a></li>
                    </c:if>

                </c:forEach>
                <c:if test="${pageInfo.hasNextPage}">
                    <li><a href="${pageContext.request.contextPath}/emps?pn=${pageInfo.pageNum+1}">&raquo;</a></li>
                </c:if>

                <li><a href="${pageContext.request.contextPath}/emps?pn=${pageInfo.pages}">Last </a></li>
            </ul>
        </div>
    </div>

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

6.3. Transform the paging query with Ajax and return the data in the form of json string

  • Application scenarios of Ajax

1. Page pull to load more data
2. No refresh paging for list data
3. Form item out of focus data validation
4. Search box prompt text drop-down list

  • controller
    @Autowired
    EmployeeService employeeService;
    //Return data in the form of json string
    @RequestMapping("/emps")
    @ResponseBody
    public PageInfo getEmpsWithJson(@RequestParam(value="pn",defaultValue = "1")Integer pn){
        //Introduce pagehelper paging plug-in
        //Before the query is called, the default value of the incoming pn is 1, pageSize is 5, meaning first pages, and 5 pages per page.
        PageHelper.startPage(pn,5);
        List<Employee> employeeList=employeeService.getAll();
        //Using PageInfo to wrap the query results, you only need to give PageInfo to the page.
        //It encapsulates the detailed paging information, including the data userList we query, and the number of pages passed into the continuous display 5
        PageInfo page = new PageInfo(employeeList,5);
        return page;
    }
  • json string returned to the client
  • Build a general object to facilitate general operation
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Msg {
    //Status code
    private int code;
    //Prompt information
    private String msg;
    //Data returned by the user to the browser
    private Map<String,Object> extend=new HashMap<>();
    public static Msg success(){
        Msg result=new Msg();
        result.setCode(100);
        result.setMsg("Successful processing");
        return result;
    }
    public static Msg fail(){
        Msg result=new Msg();
        result.setCode(200);
        result.setMsg("Processing failed");
        return result;
    }
    public Msg add(String key, Object value){
        this.getExtend().put(key,value);
        return this;
    }
}
  • Modify controller
    @Autowired
    EmployeeService employeeService;
    //Return data in the form of json string
    @RequestMapping("/emps")
    @ResponseBody
    public Msg getEmpsWithJson(@RequestParam(value="pn",defaultValue = "1")Integer pn){
        //Introduce pagehelper paging plug-in
        //Before the query is called, the default value of the incoming pn is 1, pageSize is 5, meaning first pages, and 5 pages per page.
        PageHelper.startPage(pn,5);
        List<Employee> employeeList=employeeService.getAll();
        //Using PageInfo to wrap the query results, you only need to give PageInfo to the page.
        //It encapsulates the detailed paging information, including the data userList we query, and the number of pages passed into the continuous display 5
        PageInfo page = new PageInfo(employeeList,5);
        return Msg.success().add("pageInfo",page);
    }
  • result

6.3.1. In index JSP uses Ajax to parse json strings and complete related businesses

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>Title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <!-- introduce Bootstrap -->
    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
    <!-- jQuery (Bootstrap of JavaScript The plug-in needs to be imported jQuery) -->
    <script src="https://code.jquery.com/jquery.js"></script>
    <!-- Include all compiled plug-ins -->
    <script src="js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
    <div class="row">
        <div class="col-md-12">
            <h1>SSM-CRUD</h1>
        </div>
    </div>
    <div class="row">
        <div class="col-md-4 col-md-offset-12">
            <button class="btn btn-success" >newly added</button>
            <button class="btn btn-danger">delete</button>
        </div>
    </div>
    <div class="row">
        <div class="col-md-12">
            <table class="table table-hover table-striped" id="emps_table">
                <thead>
                    <tr>
                        <th>Employee number</th>
                        <th>Employee name</th>
                        <th>Employee gender</th>
                        <th>mailbox</th>
                        <th>Employee Department</th>
                        <th>Operation button</th>
                    </tr>
                </thead>
                <tbody >

                </tbody>

            </table>
        </div>
    </div>
    <div class="row">
        <div class="col-md-6" id="page_info_area">

        </div>
        <div class="col-md-6" id="page_nav_area">

        </div>
    </div>
    <script type="text/javascript">
        $(function () {
        //    After the page is loaded, send an Ajax request directly to the paging data
            to_page(1);

        });
        //Query method
        function to_page(pn) {
            $.ajax({
                url:"${pageContext.request.contextPath}/emps",//Requested address
                data:"pn="+pn,//Requested data
                type:"GET",//Type of request
                success:function (result) {
                    // console.log(result)
                    //1. Parse and display employee data
                    build_emps_table(result);
                    //2. Parse and display paging information
                    build_page_info(result);
                    //3. Parse and display paging bar data source
                    build_page_nav(result);

                }

            });
        }
        //Parse and display employee data
        function build_emps_table(result){
            //Empty table
            $("#emps_table tbody").empty();

            var emps=result.extend.pageInfo.list;
            $.each(emps,function (index,item) {
                var empIdTd=$("<td></td>").append(item.empId);
                var empNameTd=$("<td></td>").append(item.empName);
                var genderTd=$("<td></td>").append(item.gender);
                var emailTd=$("<td></td>").append(item.email);
                var deptNameTd=$("<td></td>").append(item.department.deptName);
                //btn button
                var editBtn=$("<button></button>").addClass("btn btn-success btn-sm").append($("<span></span>").addClass("glyphicon glyphicon-pencil")).append("edit");
                var delBtn=$("<button></button>").addClass("btn btn-danger btn-sm").append($("<span></span>").addClass("glyphicon glyphicon-trash")).append("delete");
                var btnTd=$("<td></td>").append(editBtn).append(delBtn);

                $("<tr></tr>").append(empIdTd).append(empNameTd).append(genderTd).append(emailTd).append(deptNameTd).append(btnTd).appendTo($("#emps_table tbody"));
            });
        }
        //Parsing and displaying paging information
        function  build_page_info(result){
            $("#page_info_area").empty();
            $("#page_info_area").append(" current page number ["+ result.extend.pageInfo.pageNum +"], total ["+ result.extend.pageInfo.pages +"] pages, total ["+ result.extend.pageInfo.total +"] records ");
        }
        //Parse and display paging information
        function  build_page_nav(result){
            $("#page_nav_area").empty();
            var ul=$("<ul></ul>").addClass("pagination");
            //First li
            var firstPageLi=$("<li></li>").append($("<a></a>").append("home page").attr("href","#"));
            var prePageLi=$("<li></li>").append($("<a></a>").append("&laquo;"));
            //Judge whether it is the first page at this time. If so, the function of switching page number forward at this time cannot be used
            if(result.extend.pageInfo.hasPreviousPage==false){
                firstPageLi.addClass("disabled");
                prePageLi.addClass("disabled");
            }else{
                //Bind click event
                firstPageLi.click(function () {
                    to_page(1);
                });
                prePageLi.click(function () {
                    to_page(result.extend.pageInfo.pageNum-1)
                });
            }

            var nextPageLi=$("<li></li>").append($("<a></a>").append("&raquo;"));
            var lastPageLi=$("<li></li>").append($("<a></a>").append("Last ").attr("href","#"));

            //Judge whether it is the last page at this time. If so, the function of switching page number forward at this time cannot be used
            if(result.extend.pageInfo.hasNextPage==false){
                nextPageLi.addClass("disabled");
                lastPageLi.addClass("disabled");
            }else{
                //Add click events to the next and last pages
                nextPageLi.click(function () {
                    to_page(result.extend.pageInfo.pageNum+1)
                });
                lastPageLi.click(function () {
                    to_page(result.extend.pageInfo.pages);
                });
            }

            ul.append(firstPageLi).append(prePageLi);
            //Traverse page number
            $.each(result.extend.pageInfo.navigatepageNums,function (index,item) {
                var numLi=$("<li></li>").append($("<a></a>").append(item));
                if(result.extend.pageInfo.pageNum==item){
                    numLi.addClass("active");
                }
                numLi.click(function () {
                    to_page(item);
                });
                ul.append(numLi);
            });
            ul.append(nextPageLi).append(lastPageLi);
            var navEle=$("<nav></nav>").append(ul);
            navEle.appendTo($("#page_nav_area"));
        }
    </script>
</div>
</body>
</html>

6.3.2 problems encountered

  • Solve problem 1 (the previous data does not disappear after clicking)
  • Solution (clear the previous data before each query)
$("#emps_table tbody").empty();
$("#page_info_area").empty();
$("#page_nav_area").empty();

Since the previous data always exists because the data is attached every time it is obtained, the previous data should be cleared

  • Solve problem 2 (when the current page number is the first or last page, click the previous or next page, the current page number still jumps, and unreasonable parameters appear)
  • Solution (set through the properties of pageHelper plug-in)
    <plugins>
        <plugin interceptor="com.github.pagehelper.PageInterceptor">
<!--            set a property reasonable by true When, pageNum<=0 The default is the first page-->
            <property name="reasonable" value="true"/>
        </plugin>
    </plugins>
  • Solve problem 3 (the difference between Ajax query (parsing json string) and model query)

The magic of Ajax

  1. Using Ajax, the page will not be refreshed. AJAX is a way to update some web pages without loading the whole web page, and the model will reconstruct the page (request forwarding or request redirection)
  2. Customers can see the data that needs to be changed in a shorter time. The server only needs to process a single task without generating the whole page
  3. More processing is placed on the client side, which means that JavaScript will be used frequently for development

7, New business

  • backdrop:"static" specifies a static background. When the user clicks outside the modal box, the modal box will not be closed.

7.1. Add mode box

<!-- Modal -->
<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">Add employee</h4>
            </div>
            <div class="modal-body">
                <form class="form-horizontal" role="form">
                    <div class="form-group">
                        <label for="empName" class="col-sm-2 control-label">name</label>
                        <div class="col-sm-10">
                            <input type="text" name="empName" class="form-control" id="empName" placeholder="Please enter your name">
                            <span  class="help-block"></span>
                        </div>
                    </div>
                    <div class="form-group">
                        <label for="email" class="col-sm-2 control-label">mailbox</label>
                        <div class="col-sm-10">
                            <input type="text" name="email" class="form-control" id="email" placeholder="xxx@qq.com">
                            <span  class="help-block"></span>
                        </div>
                    </div>
                    <div class="form-group">
                        <label class="col-sm-2 control-label">Gender</label>
                        <div class="col-sm-10">
                            <label class="radio-inline">
                                <input type="radio" name="gender" id="gender1" value="male" checked>male
                            </label>
                            <label class="radio-inline">
                                <input type="radio" name="gender" id="gender2"  value="female">female
                            </label>
                        </div>
                    </div>
                    <div class="form-group">
                        <label class="col-sm-2 control-label">department</label>
<%--                        Department needs dynamic query--%>
                        <div class="col-sm-4">
                            <select class="form-control" name="dId" id="dept_add_select">

                            </select>
                        </div>
                    </div>
                </form>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-default" data-dismiss="modal">close</button>
                <button type="button" class="btn btn-primary" id="emp_save_btn">preservation</button>
            </div>
        </div>
    </div>
</div>

7.2. Add a button binding event to the employee, and the modal box pops up

    //    Add a button binding event to the employee, and the modal box pops up
        $("#emp_add_modal_btn").click(function () {
             //Clear the form data (form reset). jquery has no reset method and needs to take out the dom element
            reset_form("#empAddModal form");
            getDept();
            $("#empAddModal").modal({backdrop:"static"});
        });
                // Form complete reset (form content, form style reset) method
        function reset_form(ele){
            //Reset form content
            $(ele)[0].reset();
            //Reset form style
            $(ele).find("*").removeClass("has-error has-success");
            $(ele).find(".help-block").text("");
        }

7.3. Trigger getDept() when the modal box pops up; Add the option component to the drop-down box component

        function getDept(){
            $.ajax({
                url:"${pageContext.request.contextPath}/depts",
                type:"GET",
                success:function (result){
                    $("#dept_add_select").empty();
                    //Display department information in the drop-down list
                    $.each(result.extend.departments,function () {
                        var optionEle=$("<option></option>").append(this.deptName).attr("value",this.deptId);
                        optionEle.appendTo($("#dept_add_select"));
                    });
                }
            });
        }

7.4. Department information needs to be detected in real time. Ajax is used to initiate response, and there is no need to reconstruct the page

  • The controller layer calls the service layer
@Controller
public class DepartmentController {

    @Autowired
    private DepartmentService departmentService;

    //Return all department information
    @RequestMapping("/depts")
    @ResponseBody
    public Msg getDepts(){
        List<Department> list=departmentService.getDepts();
        return Msg.success().add("departments",list);
    }
}
  • The service layer calls the dao layer
@Service
public class DepartmentService {

    @Autowired
    private DepartmentMapper departmentMapper;
    public List<Department> getDepts() {
        List<Department> departments = departmentMapper.selectByExample(null);
        return departments;
    }
}

7.5 click the Save button to add the information to the database and initiate a request to verify whether the data is legal

        $("#emp_save_btn").click(function () {
            //1, The data to be submitted needs to be verified
            if(!validate_add_form()){
                return false;
            }
            //2, Judge the status of the Save button. If it is error, return false;
            if(($(this).attr("ajax-va"))=="error"){
                return false;
            }
            //III
            //1. The form data filled in the modal box is submitted to the server for saving
            //2. Send Ajax request to save employee
            //The serialize() method in jquery can serialize table content strings for Ajax requests
            $.ajax({
                url:"${pageContext.request.contextPath}/emp",
                type:"POST",
                data:$("#empAddModal form").serialize(),
                success:function (result) {
                    //Employee preservation
                    //1. Close modal box
                    $("#empAddModal").modal("hide");
                    //2. Go to the last page and display the saved data
                    //You can jump with the total number of records
                    to_page(999999999);
                }
            });
        });
  • 7.5.1 start to check whether the input data is legal
        // validate form 
        function validate_add_form(){
            //Employee name
            var name=$("#empName").val();
            var regName=/(^[a-zA-Z0-9_-]{5,8}$)|([\u2E80-\u9FFF]{2,5})/;

            if(!regName.test(name)){
                show_validate_msg("#empName","error "," the format of the name you entered is illegal ");
                return false;
            }else{
                show_validate_msg("#empName","success","");
            };
            //Employee email
            var email=$("#email").val();
            var regEmail=/^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/;
            if(!regEmail.test(email)){
                show_validate_msg("#email","error "," the email format you entered is illegal ");
                return false;
            }else{
                show_validate_msg("#email","success","");

            };
            return true;
        }
        //Extract the public method to display the status information
        function show_validate_msg(ele,status,msg){
            //Clears the state of the current element
            $(ele).parent().removeClass("has-success has-error");
            if("success"==status){
                $(ele).parent().addClass("has-success");
                $(ele).next("span").text(msg);
            }else if("error"==status){
                $(ele).parent().addClass("has-error");
                $(ele).next("span").text(msg);
            }
        };
  • 7.5.2. Ajax checks whether the user name is repeated
  • The client binding changes the event function and sends an Ajax request to verify whether the user name is available
        //The server verifies whether the user name is available
        $("#empName").change(function () {
            //Send an Ajax request to verify that the user name is available
            var empName=this.value;
            $.ajax({
                url:"${pageContext.request.contextPath}/checkUser",
                data:"empName="+empName,
                type:"POST",
                success:function (result) {
                    if(result.code==100){
                        //success
                        show_validate_msg("#empName","success "," user name available ");
                        //Give the employee button a custom attribute
                        $("#emp_save_btn").attr("ajax-va","success");
                    }else{
                        //fail
                        show_validate_msg("#empName","error "," user name unavailable ");
                        //The Save button should be disabled when the user name is not available
                        $("#emp_save_btn").attr("ajax-va","error");

                    }
                }
            });
        });
  • The server receives the request and starts processing
  • The controller layer calls the service layer
    /**
     * Verify that the user name is available
     * @param
     * @return
     * @author Xu Baohua
     * @creed: Talk is cheap,show me the code
     * @date 2021/5/31 9:27
     */
    @RequestMapping("/checkUser")
    @ResponseBody
    public Msg checkUser(@RequestParam("empName") String empName){
        boolean flag=employeeService.checkUser(empName);
        if(flag){
            //available
            return Msg.success();
        }else{
            return Msg.fail();
        }

    }
  • The service layer calls the dao layer to complete the query
    /**
     * Verify that the employee name is available
     * @param empName
     * @return true : Indicates that the current name is available, otherwise false indicates that it is not available
     * @author Xu Baohua
     * @creed: Talk is cheap,show me the code
     * @date 2021/5/31 9:35
     */
    public boolean checkUser(String empName) {
        EmployeeExample employeeExample = new EmployeeExample();
        //Specify query rules
        EmployeeExample.Criteria criteria = employeeExample.createCriteria();
        criteria.andEmpNameEqualTo(empName);
        long count = employeeMapper.countByExample(employeeExample);
        return count==0;
    }

Problems encountered:
1. The form is repeatedly submitted (when the user submits the request, the browser will record all the information of the last request. When the user presses the function key F5, the last request recorded by the browser will be initiated), and the form style should also be cleared
Solution: when clicking the Add button, reset all the form data in the modal box

                   // Form complete reset (form content, form style reset) method
        function reset_form(ele){
            //Reset form content
            $(ele)[0].reset();
            //Reset form style
            $(ele).find("*").removeClass("has-error has-success");
            $(ele).find(".help-block").text("");
        }

2. If the user name is not available, click the Save button to not submit the form data
Solution: add a user-defined attribute to the Save button, and then judge the status of the current attribute before saving

            if(($(this).attr("ajax-va"))=="error"){
                return false;
            }

3. If the background verification passes, the user name is prompted to be available, but the format of the user name verified by the client is incorrect, and the prompt contents of the front and back platforms are inconsistent, which has a certain impact on the beauty of the interface
Solution: regular expression judgment is also added in the background. The judgment content is consistent with that in the foreground, and the prompt information is also consistent, so that it will not affect the appearance of the interface because the prompt information content is different


4. After the background checks the data, it is found that the user name already exists. When the mailbox is entered, the foreground will check it again. At this time, it is found that the user name is legal and the verification has passed
Solution: add a user-defined attribute to the Save button, and then judge the status of the current attribute before saving


5. Foreground verification is only to increase the user's experience, but it is not safe. You can tamper with js content to realize form verification. For example, users can disable js code


When the user name is illegal, a private attribute is added to the Save button, and the data can be stored by tampering with the attribute,

  • Extraction method
        // Form complete reset (form content, form style reset) method
        function reset_form(ele){
            //Reset form content
            $(ele)[0].reset();
            //Reset form style
            $(ele).find("*").removeClass("has-error has-success");
            $(ele).find(".help-block").text("");
        }

7.6. Verification requires jquery front-end verification + Ajax user name duplicate verification + back-end verification (JSR303 verification based on spring MVC)

  1. JSR303 verification needs to import hibernate validator and requires Tomcat7 and above servers
<!--        JSR303 check-->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>6.0.13.Final</version>
        </dependency>
  1. Mark the verification annotation on the javaBean to be verified
 	@Pattern(regexp = "(^[a-zA-Z0-9_-]{5,8}$)|([\\u2E80-\\u9FFF]{2,5})",message = "The format of the name you entered is illegal")
    private String empName;

    private String gender;
    //You can use Email annotation or customize it
    //@Pattern(regexp = "^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$",message =" the email format you entered is illegal ")
    @Email(message = "Email format error")
    private String email;
  1. Tell spring that this data needs to be verified @ Valid
    @RequestMapping(value="/emp",method = RequestMethod.POST)
    @ResponseBody
    public Msg saveEmp(@Valid Employee employee, BindingResult result){
        if(result.hasErrors()){
            Map<String,Object> map=new HashMap<>();
            //If the verification fails, a failure should be returned, and the error message of verification failure is displayed in the mode box
            List<FieldError> fieldErrors = result.getFieldErrors();
            for (FieldError fieldError : fieldErrors) {
                System.out.println("Wrong field name"+fieldError.getField());
                System.out.println("Wrong message"+fieldError.getDefaultMessage());
                map.put(fieldError.getField(),fieldError.getDefaultMessage());
            }

            return Msg.fail().add("errorField",map);
        }else{
            employeeService.saveEmp(employee);
            return Msg.success();
        }

    }
  1. The analog front-end verification fails, and the data cannot be stored at this time
        $("#emp_save_btn").click(function () {
            //The data to be submitted needs to be verified
            // if(!validate_add_form()){
            //     return false;
            // }
            if(($(this).attr("ajax-va"))=="error"){
                return false;
            }
            //Judge whether the previous Ajax user name verification is successful. If successful, continue to execute,

            //1. The form data filled in the modal box is submitted to the server for saving
            //2. Send Ajax request to save employee
            //The serialize() method in jquery can serialize table content strings for Ajax requests
            $.ajax({
                url:"${pageContext.request.contextPath}/emp",
                type:"POST",
                data:$("#empAddModal form").serialize(),
                success:function (result) {
                    if(result.code==100){
                        //Employee preservation
                        //1. Close modal box
                        $("#empAddModal").modal("hide");
                        //2. Go to the last page and display the saved data
                        //You can jump with the total number of records
                        to_page(999999999);
                    }else{
                        //Display failure information
                        if(undefined!=result.extend.errorField.email){
                            //Display mailbox error message
                            show_validate_msg("#email","error",result.extend.errorField.email);

                        }
                        if(undefined!=result.extend.errorFields.empName){
                            //Display name error message
                            show_validate_msg("#empName","error",result.extend.errorFields.empName);
                        }

                    }

                }
            });
        });

7.7 the server receives the request to save information and starts processing

  • The controller layer calls the service layer
    /**
     * Save user information
     * @param
     * @return
     * @author Xu Baohua
     * @creed: Talk is cheap,show me the code
     * @date 2021/5/30 12:49
     */
    @RequestMapping(value="/emp",method = RequestMethod.POST)
    @ResponseBody
    public Msg saveEmp(Employee employee){
        employeeService.saveEmp(employee);
        return Msg.success();
    }
  • The service layer calls the dao layer to finish saving
    //Employee retention method
    public void saveEmp(Employee employee) {
        employeeMapper.insertSelective(employee);
    }

8, Modify business

8.1. Bind the click event to the Edit button, and the user modified mode box will pop up

Since data and buttons are created after an Ajax request is initiated, care should be taken when Binding click events
Solution:
1. Can be bound when creating a button
2. Bind and click the live() method to attach an event handler function to all matching elements. Even if this element is added later, it can still take effect. Since there is no live method in the new version of jQuery, you can use the on method instead

        $(document).on("click",".edit_btn",function () {
            //Clear the form data (form complete reset (form content, form style reset)), jquery has no reset method and needs to remove the dom element
            reset_form("#empUpdateModal form");

            getDept("#dept_update_select");

            $("#empUpdateModal").modal({
                backdrop:"static"});
        });
  • Modal box
<%--Modal box for employee modification--%>
<!-- Modal -->
<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" id="myUpdateModalLabel">Employee modification</h4>
            </div>
            <div class="modal-body">
                <form class="form-horizontal" role="form">
                    <div class="form-group">
                        <label for="empName" class="col-sm-2 control-label">name</label>
                        <div class="col-sm-10 ">
                            <p class="form-control-static" id="empName_update_static"></p>
                            <span  class="help-block"></span>
                        </div>

                    </div>
                    <div class="form-group">
                        <label for="email" class="col-sm-2 control-label">mailbox</label>
                        <div class="col-sm-10">
                            <input type="text" name="email" class="form-control" id="email_update_input" placeholder="xxx@qq.com">
                            <span  class="help-block"></span>
                        </div>
                    </div>
                    <div class="form-group">
                        <label class="col-sm-2 control-label">Gender</label>
                        <div class="col-sm-10">
                            <label class="radio-inline">
                                <input type="radio" name="gender" id="gender1_update_input" value="male" checked>male
                            </label>
                            <label class="radio-inline">
                                <input type="radio" name="gender" id="gender2_update_input"  value="female">female
                            </label>
                        </div>
                    </div>
                    <div class="form-group">
                        <label class="col-sm-2 control-label">department</label>
                        <%--                        Department needs dynamic query--%>
                        <div class="col-sm-4">
                            <select class="form-control" name="dId" id="dept_update_select">

                            </select>
                        </div>
                    </div>
                </form>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-default" data-dismiss="modal">close</button>
                <button type="button" class="btn btn-primary" id="emp_update_btn">to update</button>
            </div>
        </div>
    </div>
</div>

8.2. Initiate an Ajax request to get the data of the current employee to be modified

  • controller layer
    @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);
    }
  • service layer
    //Query employee information
    public Employee getEmp(Integer id) {
        Employee employee = employeeMapper.selectByPrimaryKey(id);
        return employee;
    }
  • View display layer
        $(document).on("click",".edit_btn",function () {
            //Clear the form data (form complete reset (form content, form style reset)), jquery has no reset method and needs to remove the dom element
            reset_form("#empUpdateModal form");

            getDept("#dept_update_select");
            //How to get the id value
            //You can add a custom attribute to the Edit button: editbtn attr("edit_id",item.empId);
            getEmp($(this).attr("edit_id"));
            $("#empUpdateModal").modal({
                backdrop:"static"});


        });
        function getEmp(id){
            $.ajax({
                url:"${pageContext.request.contextPath}/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]);
                    $("#dept_update_select").val([empData.dId]);
                }
            })
        }

8.3. Initiate Ajax request to save the data of the current employee to be modified

  • To use RestURl style, you need to use it on the web Configuration in XML
<!--    use Rest Stylized URL-->
    <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>
  • controller layer
    //At this time, note that the name of this id value should be the same as the id name in the bean object, otherwise it will not be modified
    @RequestMapping(value = "/emp/{empId}",method = RequestMethod.PUT)
    @ResponseBody
    public Msg saveEmp(Employee employee){
        employeeService.updateEmp(employee);
        return Msg.success();
    }
  • service layer
    //Modify employee information
    public void updateEmp(Employee employee) {
        employeeMapper.updateByPrimaryKeySelective(employee);
    }
  • View display layer
        //Click the update button to update the employee information
        $("#emp_update_btn").click(function () {
            //Verify whether the mailbox is legal
            var email=$("#email_update_input").val();
            var regEmail=/^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/;
            if(!regEmail.test(email)){
                show_validate_msg("#email_update_input","error "," the email format you entered is illegal (66666 ");
                return false;
            }else{
                show_validate_msg("#email_update_input","success","");

            };
            //Send Ajax request to save employee data
            $.ajax({
                url:"${pageContext.request.contextPath}/emp/"+$(this).attr("edit_id"),
                type:"POST",
                data:$("#empUpdateModal form").serialize()+"&_method=PUT",
                success:function (result) {
                    $("#empUpdateModal").modal("hide");
                    to_page(currentPage);
                }

            });
        });
  • Send POST request

If you directly send a request in the form of Ajax=PUT, there will be a problem. You will find that there is data in the request body, but the employee object cannot be encapsulated. The root cause is an error in the splicing of SQL statements
Reason: because of Tomcat
1. Tomcat encapsulates the data in the request body into a map
2,request.getParameter("empName") will take value from this map
3. When spring MVC encapsulates pojo objects, the value of each attribute in pojo is obtained by calling request getParameter()
The underlying reason is that ajax sends a PUT request, and the data in the request body passes through request Getparameter() cannot be obtained. If Tomcat finds that it is a PUT request, it will not encapsulate the data in the request body as a map

9, Delete business

9.1. Initiate ajax request to delete

  • controller layer
    //Delete employee
    @RequestMapping(value = "/emp/{id}",method = RequestMethod.DELETE)
    @ResponseBody
    public Msg deleteEmpById(@PathVariable("id") Integer id){
        employeeService.deleteEmp(id);
        return Msg.success();
    }
  • service layer
    public void deleteEmp(Integer id) {
        employeeMapper.deleteByPrimaryKey(id);
    }
  • View display layer
        $(document).on("click",".delete_btn",function () {
            var empName=$(this).parents("tr").find("td:eq(1)").text();
            var empId=$(this).attr("del_id");
            if(confirm("Confirm deletion["+empName+"]Do you?")){
                //Confirm, send ajax request to delete
                //Add a custom attribute delbtn. For the delete button attr("del_id",item.empId);
                $.ajax({
                    url:"${pageContext.request.contextPath}/emp/"+empId,
                    type:"DELETE",
                    success:function (result) {
                        to_page(currentPage);
                    }
                });
            }
        });

9.2. Improve all deletion functions

  • controller layer (integration of single deletion and batch deletion)
    //Delete employees in a single batch
    @RequestMapping(value = "/emp/{ids}",method = RequestMethod.DELETE)
    @ResponseBody
    public Msg deleteEmpById(@PathVariable("ids") String ids){
        List<Integer> del_ids=new ArrayList<>();
        if(ids.contains("-")){
            //Batch delete
            String[] str_ids=ids.split("-");
            //Collection of assembly IDS
            for (String str_id : str_ids) {
                del_ids.add(Integer.parseInt(str_id));
            }
            employeeService.deleteBatch(del_ids);
        }else{
            Integer id = Integer.parseInt(ids);
            employeeService.deleteEmp(id);
        }

        return Msg.success();
    }
  • service layer
    //Batch delete
    public void deleteBatch(List<Integer> ids) {
        EmployeeExample employeeExample = new EmployeeExample();
        EmployeeExample.Criteria criteria = employeeExample.createCriteria();
        criteria.andEmpIdIn(ids);
        employeeMapper.deleteByExample(employeeExample);
    }
  • View display layer
 //Complete select all / deselect all button
        $("#checkAll").click(function () {
            //attr() gets checked is undefined
            //For dom native attributes, it is recommended to obtain them with the prop() method

            $(".check_item").prop("checked",$(this).prop("checked"));
        });
        //Bind click events for each check box
        $(document).on("click",".check_item",function () {
            var flag=$(".check_item:checked").length==$(".check_item").length;
            $("#checkAll").prop("checked",flag);

        });
        //Click delete all to delete in batch
        $("#emp_delete_all_btn").click(function () {
            var empNames="";
            var del_idStr="";
            $.each($(".check_item:checked"),function () {
                empNames+=$(this).parents("tr").find("td:eq(2)").text()+",";
                del_idStr+=$(this).parents("tr").find("td:eq(1)").text()+"-";
            });
            //Remove redundant symbols
            empNames=empNames.substring(0,empNames.length-1);
            del_idStr=del_idStr.substring(0,del_idStr.length-1);
            if(confirm("Are you sure you want to delete these["+empNames+"]Employees")){
                //Confirm, send ajax request to delete
                //Add a custom attribute delbtn. For the delete button attr("del_id",item.empId);
                $.ajax({
                    url:"${pageContext.request.contextPath}/emp/"+del_idStr,
                    type:"DELETE",
                    success:function (result) {
                        to_page(currentPage);
                    }
                });
            }
        });

10, Summary and experience

Keywords: Spring bootstrap

Added by Candise on Thu, 03 Feb 2022 06:55:15 +0200