第一个SSM项目

SSM-CRUD

这是我写的第一个SSM框架的项目,写得很简单,写这个博客来梳理一下自己所学的知识点。
这是GitHub项目地址
SSM:SpringMVC+Spring+MyBatis
CRUD: Create(创建) Retrieve(查询) Update(更新) Delete(删除)

功能点

• 1、分页
• 2、数据校验
• jquery前端校验+JSR303后端校验
• 3、ajax
• 4、Rest风格的URI;使用HTTP协议请求方式的动词,来表示对资源的操作(GET(查询),POST(新增),PUT(修改),DELETE(删除))

技术点

• 基础框架-SSM(SpringMVC+Spring+MyBatis)
• 数据库-MySQL
• 前端框架-bootstrap快速搭建简洁美观的界面
• 项目的依赖管理-Maven
• 分页-pagehelper
• 逆向工程-MyBatis Generator

基础环境搭建

• 1、创建一个maven工程
• 2、引入项目依赖的jar包
• spring
• springmvc
• MyBatis
• 数据库连接池,驱动包
• 其他(jstl,servlet-api,junit)
• 3、引入bootstrap前端框架
• 4、编写ssm整合的关键配置文件
• web.xml,spring,springmvc,MyBatis,使用MyBatis的逆向工程生成对应的bean以及mapper
• 5、测试mapper

项目结构

tip

Maven管理的jar包

pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.my</groupId>
<artifactId>SSM</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<!-- 分页插件 PageHelper -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.10</version>
</dependency>

<!-- MyBatis -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.7</version>
</dependency>
<!-- spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.5.RELEASE</version>
<scope>provided</scope>
</dependency>
<!-- springmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.8.0</version>
</dependency>


<!-- MyBatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.4</version>
</dependency>
<!-- MyBatis整合spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.4</version>
</dependency>
<!-- c3p0 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.41</version>
</dependency>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>

<!-- 返回json字符串的支持 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>
<!-- JSR303 数据校验 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.1.0.Final</version>
</dependency>

</dependencies>
</project>

配置文件的编写

需要配置:
web.xml、spring的配置文件、MyBatis的配置文件、springmvc配置文件、Maven逆向工程文件、外部文件。

web配置

web.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="3.0"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">

<!-- 启动spring的容器 -->
<!-- needed for ContextLoaderListener -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>

<!-- Bootstraps the root web application context before servlet initialization -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<!-- springMVC的前端控制器 拦截所有请求 这里没有使用 init-param 需要在当前目录下建立dispatcherServlet-servlet.xml spring 配置文件-->
<!-- The front controller of this Spring Web application, responsible for handling all application requests -->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

<!-- Map all requests to the DispatcherServlet for handling -->
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

<!-- 字符编码过滤器,一定要放在所有过滤器之前 -->
<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>
<!-- 官方文档 org.springframework 5.1之后使用 FormContentFilter 代替 HttpPutFormContentFilter -->
<filter>
<filter-name>FormContentFilter</filter-name>
<filter-class>org.springframework.web.filter.FormContentFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>FormContentFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--配置读取spring文件的路径-->
</web-app>

springmvc配置文件

由于web.xml文件中dispatcherServlet 没有使用 init-param 需要在当前文件下建立配置文件。
dispatcherServlet-servlet.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?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/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">

<!--SpringMVC的配置文件,包含网站跳转逻辑的控制,配置 -->
<context:component-scan base-package="com.my" 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>
<property name="suffix" value=".jsp"></property>
</bean>

<!--两个标准配置 -->
<!-- 将springmvc不能处理的请求交给tomcat -->
<mvc:default-servlet-handler/>
<!-- 能支持springmvc更高级的一些功能,JSR303校验,快捷的ajax...映射动态请求 -->
<mvc:annotation-driven/>

</beans>

spring配置文件

applicationContext.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
<?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/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">

<context:component-scan base-package="com.my">
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller" />
</context:component-scan>


<!-- Spring的配置文件,这里主要配置和业务逻辑有关的 -->
<!--=================== 数据源,事务控制,xxx ================ -->
<context:property-placeholder location="classpath:dbconfig.properties" />
<bean id="pooledDataSource"
class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
<property name="driverClass" value="${jdbc.driverClass}"></property>
<property name="user" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>

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

<!-- 配置扫描器,将mybatis接口的实现加入到ioc容器中 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--扫描所有dao接口的实现,加入到ioc容器中 -->
<property name="basePackage" value="com.my.crud.dao"></property>
</bean>

<!-- 配置一个可以执行批量的sqlSession -->
<bean id="sqlSession"
class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg name="sqlSessionFactory"
ref="sqlSessionFactory"></constructor-arg>
<constructor-arg name="executorType" value="BATCH"></constructor-arg>
</bean>
<!--============================================= -->
<!-- ===============事务控制的配置 ================ -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--控制住数据源 -->
<property name="dataSource" ref="pooledDataSource"></property>
</bean>
<!--开启基于注解的事务,使用xml配置形式的事务(必要主要的都是使用配置式) -->
<aop:config>
<!-- 切入点表达式 -->
<aop:pointcut
expression="execution(* com.my.crud.service..*(..))" id="txPoint" />
<!-- 配置事务增强 -->
<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>

<!-- Spring配置文件的核心点(数据源、与mybatis的整合,事务控制) -->
</beans>

MyBatis配置文件

mybatis-config.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 驼峰命名法 -->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

<typeAliases>
<package name="com.my.crud.bean"/>
</typeAliases>

<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!--分页参数合理化 -->
<property name="reasonable" value="true"/>
</plugin>
</plugins>

</configuration>

Maven逆向工程文件

MBG.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<?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>
<property name="suppressAllComments" value="true" />
</commentGenerator>
<!-- 配置数据库连接 -->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/student" userId="root"
password="123456">
</jdbcConnection>

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

<!-- 指定javaBean生成的位置 -->
<javaModelGenerator targetPackage="com.my.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接口生成的位置,mapper接口 -->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.my.crud.dao" targetProject=".\src\main\java">
<property name="enableSubPackages" value="true" />
</javaClientGenerator>


<!-- table指定每个表的生成策略 -->
<table tableName="user" domainObjectName="Student"></table>
<table tableName="pos" domainObjectName="Pos"></table>
</context>
</generatorConfiguration>

外部文件

这里存放了连接数据库的数据。
dbconfig.properties

1
2
3
4
jdbc.jdbcUrl=jdbc:mysql://localhost:3306/student
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.user=root
jdbc.password=123456

使用逆向工程生成文件

使用MyBatis Generator。

MyBatis通过Java代码生成逆向文件

MBG.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package MBGTest;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.internal.DefaultShellCallback;

public class MBG {

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);
}
}

给逆向生成的Mapper文件添加方法

在使用逆向生成的Mapper时,只能进行单个表的查询,不能进行级联查询,需要自行添加查询方法,我这里因为学生信息里包含了职位的ID,需要进行级联查询,自行在Mapper文件中添加了查询方法和返回结果集,需要在生成的XXMapper.java 中添加相关方法。
StudentMapper.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<!-- 包含职位信息 自定义结果集-->
<resultMap type="com.my.crud.bean.Student" id="WithposResultMap">
<id column="id" jdbcType="INTEGER" property="id" />
<result column="name" jdbcType="VARCHAR" property="name" />
<result column="sex" jdbcType="VARCHAR" property="sex" />
<result column="posit" jdbcType="INTEGER" property="posit" />
<!-- 级联查询 -->
<association property="pos" javaType="com.my.crud.bean.Pos">
<id column="sid" property="sid"/>
<result column="position" property="position"/>
</association>
</resultMap>
<!-- 将重复语句抽取出来 -->
<sql id="Withpos_Column_List">
id, name, sex, posit,sid,position
</sql>

<!-- 带职位的查询 -->
<!-- List<Student> selectByExampleWithpos(StudentExample example); -->
<select id="selectByExampleWithpos" resultMap="WithposResultMap">
select
<if test="distinct">
distinct
</if>
<include refid="Withpos_Column_List" />
from user left JOIN pos on posit=sid
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
<if test="orderByClause != null">
order by ${orderByClause}
</if>
</select>
<!-- Student selectByPrimaryKeyWithpos(Integer id); -->
<select id="selectByPrimaryKeyWithpos" resultMap="WithposResultMap">
select
<include refid="Withpos_Column_List" />
from user left JOIN pos on posit=sid
where id = #{id,jdbcType=INTEGER}
</select>

正式开始

前面配置文件准备完成和测试完成后,就可以开始正式开始写项目了,这里在jsp文件中编写的页面,使用了bootstrap,非常的简陋。

返回json的处理

MSG.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package com.my.crud.bean;

import java.util.HashMap;
import java.util.Map;

/**
* 通用的返回类
*
*/
public class Msg {
//状态码 100-成功 200-失败
private int code;
//提示信息
private String msg;
//用户返回给服务器的数据
private Map<String,Object> extend = new HashMap<String, Object>();

public static Msg success() {
Msg result = new Msg();
result.setCode(100);
result.setMsg("处理成功");
return result;
}

public static Msg fail() {
Msg result = new Msg();
result.setCode(200);
result.setMsg("处理失败");
return result;
}

public Msg add(String key,Object value) {
this.getExtend().put(key, value);
return this;
}

public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public Map<String, Object> getExtend() {
return extend;
}
public void setExtend(Map<String, Object> extend) {
this.extend = extend;
}
}

职位信息的处理

由于只是一个查询职位信息,单独写出来。
Poscontroller.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.my.crud.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.my.crud.bean.Msg;
import com.my.crud.bean.Pos;
import com.my.crud.service.PosService;

@Controller
public class Poscontroller {

@Autowired
private PosService posService;

@ResponseBody
@RequestMapping("/positAll")
public Msg getPosAll() {
List<Pos> list = posService.getPosAll();
return Msg.success().add("posits", list);
}
}

PosService.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.my.crud.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.my.crud.bean.Pos;
import com.my.crud.dao.PosMapper;

@Service
public class PosService {

@Autowired
private PosMapper posMapper;

public List<Pos> getPosAll(){
List<Pos> list = posMapper.selectByExample(null);
return list;
}
}

查询

• 1、访问index.jsp页面
• 2、index.jsp页面发送出查询学生列表请求
• 3、Studentcontroller来接受请求,查出学生数据,返回json
• 4、js解析json填充数据
• 5、pageHelper分页插件完成分页查询功能
• URI:/stus

查询-ajax

• 1、index.jsp页面直接发送ajax请求进行员工分页数据的查询
• 2、服务器将查出的数据,以json字符串的形式返回给浏览器
• 3、浏览器收到js字符串。可以使用js对json进行解析,使用js通过dom增删改改变页面。
• 4、返回json。实现客户端的无关性。

主页展示

tip

主页代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>学生列表</title>
<%
pageContext.setAttribute("APP_PATH", request.getContextPath());
%>
<!-- web路径:
不以/开始的相对路径,找资源,以当前资源的路径为基准,经常容易出问题。
以/开始的相对路径,找资源,以服务器的路径为标准(http://localhost:3306);需要加上项目名
http://localhost:3306/crud
-->
<script type="text/javascript"
src="${APP_PATH }/static/js/jquery-3.5.1.min.js"></script>
<link
href="${APP_PATH }/static/bootstrap-3.3.7-dist/css/bootstrap.min.css"
rel="stylesheet">
<script
src="${APP_PATH }/static/bootstrap-3.3.7-dist/js/bootstrap.min.js"></script>
</head>
<!-- 搭建显示页面 -->
<div class="container">
<!-- 标题 -->
<div class="row">
<div class="col-md-12">
<h1>SSM-CRUD</h1>
</div>
</div>
<!-- 按钮 -->
<div class="row">
<div class="col-md-4 col-md-offset-8">
<button class="btn btn-primary" id="stu_add_modal_btn">新增</button>
<button class="btn btn-danger" id="stu_delete_all_btn">删除</button>
</div>
</div>
<!-- 显示表格数据 -->
<div class="row">
<div class="col-md-12">
<table class="table table-hover" id="stus_table">
<thead>
<tr>
<th><input type="checkbox" id="check_all" /></th>
<th>#</th>
<th>Name</th>
<th>Sex</th>
<th>Position</th>
<th>操作</th>
</tr>
</thead>
<tbody>

</tbody>
</table>
</div>
</div>
<!-- 显示分页信息 -->
<div class="row">
<!--分页文字信息 -->
<div class="col-md-6" id="page_info_area"></div>
<!-- 分页条信息 -->
<div class="col-md-6" id="page_nav_area"></div>
</div>

</div>
<script type="text/javascript">

var totalRecord,currentPage;
//1、页面加载完成以后,直接去发送ajax请求,要到分页数据
$(function(){
//去首页
to_page(1);
});

function to_page(pn){
$.ajax({
url:"${APP_PATH}/stus",
data:"pn="+pn,
type:"GET",
success:function(result){
//console.log(result);
//1、解析并显示学生数据
build_stus_table(result);
//2、解析并显示分页信息
build_page_info(result);
//3、解析显示分页条数据
build_page_nav(result);
}
});
}

function build_stus_table(result){
//清空table表格
$("#stus_table tbody").empty();
var stus = result.extend.pageInfo.list;
$.each(stus,function(index,item){
var checkBoxTd = $("<td><input type='checkbox' class='check_item'/></td>");
var IdTd = $("<td></td>").append(item.id);
var NameTd = $("<td></td>").append(item.name);
var SexTd = $("<td></td>").append(item.sex);
var PosTd = $("<td></td>").append(item.pos.position);

var editBtn = $("<button></button>").addClass("btn btn-primary btn-sm edit_btn")
.append($("<span></span>").addClass("glyphicon glyphicon-pencil")).append("编辑");
//为编辑按钮添加一个自定义的属性,来表示当前学生id
editBtn.attr("edit-id",item.id);
var delBtn = $("<button></button>").addClass("btn btn-danger btn-sm delete_btn")
.append($("<span></span>").addClass("glyphicon glyphicon-trash")).append("删除");
//为删除按钮添加一个自定义的属性来表示当前删除的学生id
delBtn.attr("del-id",item.id);
var btnTd = $("<td></td>").append(editBtn).append(" ").append(delBtn);
//var delBtn =
//append方法执行完成以后还是返回原来的元素
$("<tr></tr>").append(checkBoxTd)
.append(IdTd)
.append(NameTd)
.append(SexTd)
.append(PosTd)
.append(btnTd)
.appendTo("#stus_table tbody");
});
}
//解析显示分页信息
function build_page_info(result){
$("#page_info_area").empty();
$("#page_info_area").append("当前"+result.extend.pageInfo.pageNum+"页,总"+
result.extend.pageInfo.pages+"页,总"+
result.extend.pageInfo.total+"条记录");
//总记录数
totalRecord = result.extend.pageInfo.total;
//当前页
currentPage = result.extend.pageInfo.pageNum;
}
//解析显示分页条,点击分页要能去下一页....
function build_page_nav(result){
//清除全选框的属性
$("#check_all").prop("checked",false);
//page_nav_area
$("#page_nav_area").empty();
var ul = $("<ul></ul>").addClass("pagination");

//构建元素
var firstPageLi = $("<li></li>").append($("<a></a>").append("首页").attr("href","#"));
var prePageLi = $("<li></li>").append($("<a></a>").append("&laquo;"));
if(result.extend.pageInfo.hasPreviousPage == false){
firstPageLi.addClass("disabled");
prePageLi.addClass("disabled");
}else{
//为元素添加点击翻页的事件
firstPageLi.click(function(){
to_page(1);
});
prePageLi.click(function(){
to_page(result.extend.pageInfo.pageNum -1);
});
}



var nextPageLi = $("<li></li>").append($("<a></a>").append("&raquo;"));
var lastPageLi = $("<li></li>").append($("<a></a>").append("末页").attr("href","#"));
if(result.extend.pageInfo.hasNextPage == false){
nextPageLi.addClass("disabled");
lastPageLi.addClass("disabled");
}else{
nextPageLi.click(function(){
to_page(result.extend.pageInfo.pageNum +1);
});
lastPageLi.click(function(){
to_page(result.extend.pageInfo.pages);
});
}



//添加首页和前一页 的提示
ul.append(firstPageLi).append(prePageLi);
//1,2,3遍历给ul中添加页码提示
$.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);

//把ul加入到nav
var navEle = $("<nav></nav>").append(ul);
navEle.appendTo("#page_nav_area");
}

主页链接处理

Studentcontroller.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 导入jackson包
* 返回json数据
* @param pn 页码
* @return
*/
@ResponseBody
@RequestMapping(value = "/stus",method = RequestMethod.GET)
public Msg getSstusWithJson(@RequestParam(value = "pn", defaultValue = "1") Integer pn) {
// 这不是一个分页查询;
// 引入PageHelper分页插件
// 在查询之前只需要调用,传入页码,以及每页的大小
PageHelper.startPage(pn, 5);
// startPage后面紧跟的这个查询就是一个分页查询
List<Student> stus = studentService.getAll();
// 使用pageInfo包装查询后的结果,只需要将pageInfo交给页面就行了。
// 封装了详细的分页信息,包括有我们查询出来的数据,传入连续显示的页数
PageInfo page = new PageInfo(stus, 5);
return Msg.success().add("pageInfo", page);
}

StudentService.java

1
2
3
4
5
6
7
8
9
10
/**
* 查询所有学生
* @return
*/
public List<Student> getAll() {
StudentExample example = new StudentExample();
//添加排序 根据id 升序
example.setOrderByClause("id asc");
return studentMapper.selectByExampleWithpos(example);
}

新增

• 1、在index.jsp页面点击”新增”
• 2、弹出新增对话框
• 3、去数据库查询职位列表,显示在对话框中
• 4、用户输入数据,并进行校验
• jquery前端校验,ajax用户名重复校验,重要数据(后端校验(JSR303),唯一约束);
• 5、完成保存
• URI:
• /stus/{id} GET 查询学生信息
• /stus POST 保存学生信息
• /stus/{id} PUT 修改学生信息
• /stus/{id} DELETE 删除学生信息

新增按钮展示

tip

添加学生代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
<!-- 学生添加的模态框 -->
<div class="modal fade" id="stuAddModal" 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">Name</label>
<div class="col-sm-10">
<input type="text" name="name" class="form-control" id="stuName_add_input" placeholder="stuName">
<span class="help-block"></span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">Sex</label>
<div class="col-sm-10">
<label class="radio-inline">
<input type="radio" name="sex" id="sex1_add_input" value="男" checked="checked"> 男
</label>
<label class="radio-inline">
<input type="radio" name="sex" id="sex2_add_input" value="女"> 女
</label>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">Position</label>
<div class="col-sm-4">
<!-- 职位提交职位id即可 -->
<select class="form-control" name="posit">
</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="stu_save_btn">保存</button>
</div>
</div>
</div>
</div>

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

//点击新增按钮弹出模态框。
$("#stu_add_modal_btn").click(function(){
//清除表单数据(表单完整重置(表单的数据,表单的样式))
reset_form("#stuAddModal form");
//s$("")[0].reset();
//发送ajax请求,查出职位信息,显示在下拉列表中
getpositS("#stuAddModal select");
//弹出模态框
$("#stuAddModal").modal({
backdrop:"static"
});
});

//查出所有的职位信息并显示在下拉列表中
function getpositS(ele){
//清空之前下拉列表的值
$(ele).empty();
$.ajax({
url:"${APP_PATH}/positAll",
type:"GET",
success:function(result){
//{"code":100,"msg":"处理成功!",
//"extend":{"posits":[{"sid":1,"position":"班长"},{"sid":2,"position":"学习委员"}.....]}}
//console.log(result);
//显示部门信息在下拉列表中
$("#stuAddModal select").append("")
$.each(result.extend.posits,function(){
var optionEle = $("<option></option>").append(this.position).attr("value",this.sid);
optionEle.appendTo(ele);
});
}
});
}

//校验表单数据
function validate_add_form(){
//1、拿到要校验的数据,使用正则表达式
var stuName = $("#stuName_add_input").val();
//英文a-z A-Z 0-9 _ - 6到16位 or 中文 2位-5位
var regName = /(^[a-zA-Z0-9_-]{6,16}$)|(^[\u2E80-\u9FFF]{2,5})/;
if(!regName.test(stuName)){
//alert("用户名可以是2-5位中文或者6-16位英文和数字的组合");
show_validate_msg("#stuName_add_input", "error", "用户名必须是2-5位中文或者6-16位英文和数字的组合");
return false;
}else{
show_validate_msg("#stuName_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 if("error" == status){
$(ele).parent().addClass("has-error");
$(ele).next("span").text(msg);
}
}

//校验用户名是否可用
$("#stuName_add_input").change(function(){
//发送ajax请求校验用户名是否可用
var stuName = this.value;
$.ajax({
url:"${APP_PATH}/checkuser",
data:"stuName="+stuName,
type:"POST",
success:function(result){
if(result.code==100){
show_validate_msg("#stuName_add_input","success","用户名可用");
$("#stu_save_btn").attr("ajax-va","success");
}else{
show_validate_msg("#empName_add_input","error",result.extend.va_msg);
$("#stu_save_btn").attr("ajax-va","error");
}
}
});
});

//点击保存,保存学生。
$("#stu_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}/stus",
type:"POST",
data:$("#stuAddModal form").serialize(),
success:function(result){
//alert(result.msg);
if(result.code == 100){
//学生保存成功;
//1、关闭模态框
$("#stuAddModal").modal('hide');

//2、来到最后一页,显示刚才保存的数据
//发送ajax请求显示最后一页数据即可
to_page(totalRecord);
}else{
//显示失败信息
//console.log(result);
//有哪个字段的错误信息就显示哪个字段的;
if(undefined != result.extend.errorFields.name){
//显示学生名字的错误信息
show_validate_msg("#stuName_add_input", "error", result.extend.errorFields.name);
}
}
}
});
});

新增链接处理

Studentcontroller.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/**
* 数据校验
* @return
*/
@ResponseBody
@RequestMapping("/checkuser")
public Msg checkStuName(@RequestParam("stuName")String name) {
//先判断用户名是否是合法的表达式
String regex = "(^[a-zA-Z0-9_-]{6,16}$)|(^[\\u2E80-\\u9FFF]{2,5})";
if(!name.matches(regex)) {
return Msg.fail().add("va_msg", "用户名必须是2-5位中文或者6-16位英文和数字的组合");
}
//数据库校验
boolean b = studentService.checkuser(name);
if(b) {
return Msg.success();
}
else
return Msg.fail().add("va_msg", "用户名不可用");
}

/**
* 学生新增保存
* @return
*/
@ResponseBody
@RequestMapping(value = "/stus",method = RequestMethod.POST)
public Msg saveStu(@Valid Student student,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 {
studentService.saveStu(student);
return Msg.success();
}
}

StudentService.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 添加学生
* @param student
*/
public void saveStu(Student student) {
studentMapper.insertSelective(student);
}

/**
* 校验学生名是否可用
* 1.支持JSR303校验
* 2.导入hibernate-Validator
* @param name
* @return ture:可用 false:不可用
*/
public boolean checkuser(String name) {
StudentExample example = new StudentExample();
Criteria criteria = example.createCriteria();
criteria.andNameEqualTo(name);
long count = studentMapper.countByExample(example);
return count == 0;
}

修改

• 1、点击编辑
• 2、弹出用户修改的模态框(显示学生信息)
• 3、点击更新,完成用户修改

编辑(更新)展示

tips

编辑代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
//更新学生信息
$(document).on("click",".edit_btn",function(){
//alert("edit");
//1、查出职位信息,并显示职位列表
getpositS("#stuUpdateModal select");
//2、查出学生信息,显示学生信息
getStu($(this).attr("edit-id"));
//3、把学生的id传递给模态框的更新按钮
$("#stu_update_btn").attr("edit-id",$(this).attr("edit-id"));
$("#stuUpdateModal").modal({
backdrop:"static"
});
});

function getStu(id){
$.ajax({
url:"${APP_PATH}/stu/"+id,
type:"GET",
success:function(result){
//console.log(result);
var stuData = result.extend.stu;
$("#stuName_update_static").text(stuData.name);
$("#stuSex_update_static").text(stuData.sex);
$("#stuUpdateModal select").val([stuData.posit]);
}
});
}

//点击更新,更新学生信息
$("#stu_update_btn").click(function(){
//发送ajax请求保存更新的学生数据
$.ajax({
url:"${APP_PATH}/stu/"+$(this).attr("edit-id"),
type:"PUT",
data:$("#stuUpdateModal form").serialize(),
success:function(result){
//alert(result.msg);
//1、关闭对话框
$("#stuUpdateModal").modal("hide");
//2、回到本页面
to_page(currentPage);
}
});
});

编辑链接处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 更新方法
*/
@ResponseBody
@RequestMapping(value = "stu/{id}",method = RequestMethod.PUT)
public Msg updateStu(Student student) {
studentService.updateStu(student);
return Msg.success();
}

/**
* 将要修改的数据发送去页面
* @param id
* @return
*/
@ResponseBody
@RequestMapping(value = "stu/{id}",method = RequestMethod.GET)
//@PathVariable() 从路径中获取
public Msg getStu(@PathVariable("id") Integer id) {
Student student = studentService.getStu(id);
return Msg.success().add("stu", student);
}

删除

• 1、单个删除
• URI:/stu/{id} DELETE
• 2、批量删除
• URI:/stu/{拼装字符串} DELETE

删除展示

单个删除
tip
批量删除
tip

删除代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
//单个删除
$(document).on("click",".delete_btn",function(){
//1、弹出是否确认删除对话框
var stuName = $(this).parents("tr").find("td:eq(2)").text();
var id = $(this).attr("del-id");
//alert($(this).parents("tr").find("td:eq(1)").text()); 输出当前名字
if(confirm("确认删除【"+stuName+"】吗?")){
//确认,发送ajax请求删除即可
$.ajax({
url:"${APP_PATH}/stu/"+id,
type:"DELETE",
success:function(result){
alert(result.msg);
//回到本页
to_page(currentPage);
}
});
}
});

//完成全选/全不选功能
$("#check_all").click(function(){
//attr获取checked是undefined;
//dom原生的属性;attr获取自定义属性的值;
//prop修改和读取dom原生属性的值
$(".check_item").prop("checked",$(this).prop("checked"));
});

//check_item
$(document).on("click",".check_item",function(){
//判断当前选择中的元素是否5个
var flag = $(".check_item:checked").length==$(".check_item").length;
$("#check_all").prop("checked",flag);
});

//点击全部删除,就批量删除
$("#stu_delete_all_btn").click(function(){
//
var stuNames = "";
var del_idstr = "";
if($(".check_item:checked").length==0){
alert("你还未选择需要删除的学生");
return;
}
$.each($(".check_item:checked"),function(){
//this
stuNames += $(this).parents("tr").find("td:eq(2)").text()+",";
//组装学生id字符串
del_idstr += $(this).parents("tr").find("td:eq(1)").text()+"-";
});
//去除stuNames多余的,
stuNames = stuNames.substring(0, stuNames.length-1);
//去除删除的id多余的-
del_idstr = del_idstr.substring(0, del_idstr.length-1);
if(confirm("确认删除【"+stuNames+"】吗?")){
//发送ajax请求删除
$.ajax({
url:"${APP_PATH}/stu/"+del_idstr,
type:"DELETE",
success:function(result){
alert(result.msg);
//回到当前页面
to_page(currentPage);

}
});
}
});

删除链接处理

Studentcontroller.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* 删除单个或批量学生信息
* 批量删除 1-2-3 id由-隔开
*/
@ResponseBody
@RequestMapping(value = "stu/{ids}",method = RequestMethod.DELETE)
public Msg deleteStuByid(@PathVariable("ids") String ids) {
//批量删除
if(ids.contains("-")) {
List<Integer> list = new ArrayList<>();
String[] id = ids.split("-");
for (String string : id) {
list.add(Integer.parseInt(string));
}
studentService.deleteBatch(list);
}
else {
//单个删除
Integer id = Integer.parseInt(ids);
studentService.deleteStu(id);
}
return Msg.success();
}

StudentService.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 删除单个学生
* @param id
*/
public void deleteStu(Integer id) {
studentMapper.deleteByPrimaryKey(id);
}

/**
* 批量删除
* @param ids
*/
public void deleteBatch(List<Integer> ids) {
StudentExample example = new StudentExample();
Criteria criteria = example.createCriteria();
//delete from xxx where id in(1,2,3)
criteria.andIdIn(ids);
studentMapper.deleteByExample(example);
}

总结

tip

原创技术分享,您的支持将鼓励我继续创作
0%