# StudentManager **Repository Path**: louxj/StudentManager ## Basic Information - **Project Name**: StudentManager - **Description**: 简易的学生管理系统 采用SSM框架构建,可以作为SSM入门学习的第一个小项目。 - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 3 - **Forks**: 2 - **Created**: 2018-12-02 - **Last Updated**: 2025-06-15 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 技术准备 为了完成这个项目,需要掌握如下技术: - Java基础知识 - 前端:HTML, CSS, JAVASCRIPT, JQUERY - J2EE:Tomcat, Servlet, JSP, Filter - 框架:Spring, Spring MVC, MyBatis, Spring 与 MyBatis 整合, SSM 整合 - 数据库:MySQL - 开发工具:IDEA, Maven,navicat # 开发流程 ## 需求分析 首先要确定要做哪些功能 - 使用mysql数据库来保存数据. - 能增删改查学生的信息(学号,名称,年龄,性别,出生日期). ## 表结构设计 根据需求,那么只需要一个 student 表就能够完成功能了。 ### 创建数据库:student 将数据库编码格式设置为 UTF-8 ,便于存取中文数据 ```sql DROP DATABASE IF EXISTS student; CREATE DATABASE student DEFAULT CHARACTER SET utf8; ``` ### 创建学生表:student 不用学生学号(studentID)作为主键的原因是:不方便操作,例如在更新数据的时候,同时也要更改学号,那这样的操作怎么办呢? 所以我们加了一个 id 用来唯一表示当前数据。 ```sql CREATE TABLE student( id int(11) NOT NULL AUTO_INCREMENT, student_id int(11) NOT NULL UNIQUE, name varchar(255) NOT NULL, age int(11) NOT NULL, sex varchar(255) NOT NULL, birthday date DEFAULT NULL, PRIMARY KEY (id) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; ``` > MySQL 在 Windows 下不区分大小写,但在 Linux 下默认区分大小写,因此,数据库名、表明、字段名都不允许出现任何大写字母,避免节外生枝。 ## 原型设计 就是设计界面,在商业项目中,这是很重要的一步,我们可以解除界面原型,低成本、高效率的与客户达成需求的一致性。 这个项目一共就分为四个页面: - 主页面  - 新增学生信息页面  - 编译学生信息页面  - 学生信息列表展示页面  ## SSM 环境搭建 在真正开始编写代码之前,我们首先需要先来搭建好我们的 SSM 环境。 第一步:创建 Maven webapp 项目 首先新建工程,选择 Maven 标签,然后勾选上【Create from archetype】选择 webapp:  点击下一步,填写上【GroupId】和【ArtifactId】:  - GroupId:项目组织唯一的标识符,实际对应 JAVA 的包的结构,也就是 main 目录下 java 的目录结构(包)。 - AritifactId:项目的唯一标识符,实际对应项目的名称,就是项目根目录的名称。 然后是确认项目路径,可以看到 Maven 配置中的参数,不需要做改动,直接下一步就可以:  确认项目名称和路径,点击【Finish】即可: 等待一会儿,控制台就会有创建成功的提示信息,我们把【Enable Auto-Import】点上,这个提示会在每次 pom.xml 有改动时出现,自动导入,省掉麻烦:  第二步:搭建项目目录结构 下面就是 Maven 风格的 webapp 的默认目录结构:  **注意**: webapp 是默认没有 java 源文件也没有 test 目录的。 遵循 Maven 的统一项目结构,我们搭建出项目的完整目录结构如下图:  我们并没有使用 Log4j 来输出日志,而是使用 logback 日志框架。 提示:我们可以在 IDEA 中右键目录然后选择【Make Directory as】,让 IDEA 识别不同的目录作用 这里的目录建好之后还需要设置一下,让 IDEA 识别目录作用,选择【File】>【Project Structure】:  设置好之后点击 OK,即完成了项目目录的搭建。  第三步:配置文件内容 在【pom.xml】文件中声明依赖的 jar 包 : ``` 4.0.0 war StudentManager con.netease.cloud StudentManager 1.0-SNAPSHOT 1.8 1.8 UTF-8 UTF-8 4.3.5.RELEASE 3.4.1 StudentManager maven-clean-plugin 3.0.0 maven-resources-plugin 3.0.2 maven-compiler-plugin 3.7.0 ${maven.compiler.source} ${maven.compiler.target} ${project.build.sourceEncoding} maven-surefire-plugin 2.20.1 maven-war-plugin 3.2.0 maven-install-plugin 2.5.2 maven-deploy-plugin 2.8.2 javax.servlet jstl 1.2 org.apache.taglibs taglibs-standard-impl 1.2.5 javax javaee-api 7.0 junit junit 4.12 ch.qos.logback logback-classic 1.2.2 com.fasterxml.jackson.core jackson-databind 2.8.7 mysql mysql-connector-java 5.1.41 runtime com.mchange c3p0 0.9.5.2 org.mybatis mybatis ${mybatis.version} org.mybatis mybatis-spring 1.3.1 org.springframework spring-core ${spring.version} org.springframework spring-beans ${spring.version} org.springframework spring-context ${spring.version} org.springframework spring-jdbc ${spring.version} org.springframework spring-tx ${spring.version} org.springframework spring-web ${spring.version} org.springframework spring-webmvc ${spring.version} org.springframework spring-test ${spring.version} ``` 标签是默认生成的 我们在 中声明了编码格式以及使用的 spring 和 mybatis 的版本号,然后在 中声明了具体的 jar 包 在`【web.xml】`中声明编码过滤器并配置 DispatcherServlet : ```xml 学生管理系统 index.html index.jsp SetCharacterEncoding org.springframework.web.filter.CharacterEncodingFilter encoding UTF-8 forceEncoding true SetCharacterEncoding /* SpringMVC org.springframework.web.servlet.DispatcherServlet contextConfigLocation classpath:spring/spring-*.xml 1 true SpringMVC / ``` 在`【spring-mybatis.xml】`中完成 spring 和 mybatis 的配置: ```xml ``` 在`【spring-mvc.xml】`中完成 Spring MVC 的相关配置: ```xml ``` 在`【jdbc.properties】`中配置 c3p0 数据库连接池: ``` jdbc.driver=com.mysql.jdbc.Driver #数据库地址 jdbc.url=jdbc:mysql://127.0.0.1:3306/student?useUnicode=true&characterEncoding=utf8 #用户名 jdbc.username=root #密码 jdbc.password=123 #最大连接数 c3p0.maxPoolSize=30 #最小连接数 c3p0.minPoolSize=10 #关闭连接后不自动commit c3p0.autoCommitOnClose=false #获取连接超时时间 c3p0.checkoutTimeout=10000 #当获取连接失败重试次数 c3p0.acquireRetryAttempts=2 ``` 在`【logback.xml】`中完成日志输出的相关配置: ```xml %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n ``` 以上就完成了 SSM 框架的基本配置: - 添加进了 SSM 项目所需要的 jar 包 - 配置好了 spring/mybatis/spring MVC 的相关配置信息(自动扫描 `com.netease.cloud `包下的带有注解的类) - 通过 xml 配置的方式配置好了日志和数据库 ### entity的设计 实体类仅仅是对数据库中表的一一映射(表中字段名应该和实体类中的名称一一对应),同时可能还需要兼顾对业务能力的支持。 在 Packge【com.netease.cloud.entity】下创建 Student 类: ```java package com.netease.cloud.entity; import java.util.Date; /** * Student 实体类 * * @author: louxj424 * @create: 2018-12-02-下午 12:03 */ public class Student { /** * 自增主键 */ private int id; /** * 学号 */ private int studentId; /** * 姓名 */ private String name; /** * 年龄 */ private int age; /** * 性别 */ private String sex; /** * 生日 */ private Date birthday; public int getId() { return id; } public void setId(int id) { this.id = id; } public int getStudentId() { return studentId; } public void setStudentId(int studentId) { this.studentId = studentId; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } } ``` ### DAO的设计 DAO,即 Date Access Object,数据库访问对象,就是对数据库相关操作的封装,让其他地方看不到 JDBC 的代码。 在【cn.netease.cloud.dao】包下创建【StudentDao】接口: ```java package com.netease.cloud.dao; import com.netease.cloud.entity.Student; import org.springframework.stereotype.Repository; import java.util.List; @Repository public interface StudentDao { int getTotal(); int addStudent(Student student); int deleteStudent(int id); int updateStudent(Student student); Student getStudent(int id); List list(int start, int count); } ``` 然后在【resources/mapper】下创建好对应的映射文件【StudengMapper.xml】: ```xml id,student_id,name,age,sex,birthday student SELECT FROM WHERE id = #{id,jdbcType=INTEGER} SELECT FROM ORDER BY student_id DESC limit #{param1}, #{param2} SELECT COUNT(*) FROM INSERT INTO (id,student_id,name,age,sex,birthday) VALUES(#{id,jdbcType=INTEGER}, #{studentId,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR}, #{age,jdbcType=INTEGER}, #{sex,jdbcType=VARCHAR}, #{birthday,jdbcType=TIMESTAMP}) DELETE FROM WHERE id = #{id,jdbcType=INTEGER} UPDATE SET student_id = #{studentId,jdbcType=INTEGER}, name = #{name,jdbcType=VARCHAR}, age = #{age,jdbcType=INTEGER}, sex = #{sex,jdbcType=VARCHAR}, birthday = #{birthday,jdbcType=TIMESTAMP} WHERE id= #{id,jdbcType=INTEGER} ``` 编写好了 Dao 类是需要测试的,编写BaseTest和StudentDaoTest类: ```java package com.netease.cloud; import org.junit.runner.RunWith; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.transaction.annotation.Transactional; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration({"classpath:spring-mybatis-test.xml"}) @Transactional public class BaseTest { } ``` ```java package com.netease.cloud.dao; import com.netease.cloud.BaseTest; import com.netease.cloud.entity.Student; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import java.util.Date; import java.util.List; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertSame; public class StudentDaoTest extends BaseTest { private static final Logger logger = LoggerFactory.getLogger(StudentDaoTest.class); private static final Integer ID = 65523; @Autowired private StudentDao studentDao; @Test public void testAdd() { Student student = new Student(); student.setId(ID); student.setStudentId(20142126); student.setName("lou"); student.setAge(12); student.setSex("男"); student.setBirthday(new Date()); int ret = studentDao.addStudent(student); assertEquals(1, ret); } @Test public void testSelect() { testAdd(); Student student = studentDao.getStudent(ID); assertNotSame(0, student.getId()); } @Test public void testUpdate() { testAdd(); Student student = studentDao.getStudent(ID); student.setAge(100); studentDao.updateStudent(student); assertSame(100, student.getAge()); } @Test public void testDelete() { testAdd(); int ret = studentDao.deleteStudent(ID); assertEquals(1, ret); } @Test public void testGetTotal() { testAdd(); int ret = studentDao.getTotal(); assertNotSame(0, ret); } @Test public void testList() { testAdd(); List studentList = studentDao.list(0, 1); assertNotSame(0, studentList.size()); } } ``` ### service的设计 > 问题: 为什么不直接使用 Dao 类而是还要在上面封装一层 Service 层呢? >回答:基于责任分离的原则,Dao 层就应该专注于对数据库的操作,而在 Service 层我们可以增加一些非 CRUD 的方法去更好的完成本身抽离出来的 service 服务(业务处理)。 在【com.netease.cloud.service】包下创建【StudentService】接口: ```java package com.netease.cloud.service; import com.netease.cloud.entity.Student; import java.util.List; public interface StudentService { /** * 获取到 Student 的总数 * * @return */ int getTotal(); /** * 增加一条数据 * * @param student */ int addStudent(Student student); /** * 删除一条数据 * * @param id */ int deleteStudent(int id); /** * 更新一条数据 * * @param student */ int updateStudent(Student student); /** * 找到一条数据 * * @param id * @return */ Student getStudent(int id); /** * 列举出从 start 位置开始的 count 条数据 * * @param start * @param count * @return */ List list(int start, int count); } ``` 并在相同包名下新建一个文件夹【impl】并在该文件夹下创建实现类【StudentServiceImpl】实现接口【StudentService】: ```java package com.netease.cloud.service.impl; import com.netease.cloud.dao.StudentDao; import com.netease.cloud.entity.Student; import com.netease.cloud.service.StudentService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.Collections; import java.util.List; /** * StudentService 的实现类 */ @Service public class StudentServiceImpl implements StudentService { @Autowired private StudentDao studentDao; public int getTotal() { return studentDao.getTotal(); } public int addStudent(Student student) { return studentDao.addStudent(student); } public int deleteStudent(int id) { return studentDao.deleteStudent(id); } public int updateStudent(Student student) { return studentDao.updateStudent(student); } public Student getStudent(int id) { return studentDao.getStudent(id); } public List list(int start, int count) { List studentList = studentDao.list(start, count); if (studentList == null) { studentList = Collections.emptyList(); } return studentList; } } ``` ### 功能开发 在【com.netease.cloud.controller】包下创建【StudentController】控制器: ```java package com.netease.cloud.controller; import com.netease.cloud.Const; import com.netease.cloud.entity.Student; import com.netease.cloud.service.StudentService; import com.netease.cloud.util.Page; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.text.ParseException; import java.util.Date; import java.util.List; /** * Student 控制器 * * @create: 2018-12-02-下午 16:43 */ @Controller @RequestMapping("") public class StudentController { private static final Logger logger = LoggerFactory.getLogger(StudentController.class); @Autowired private StudentService studentService; @RequestMapping("/listStudent") public String listStudent(HttpServletRequest request, HttpServletResponse response) { // 获取分页参数 int start = 0; int count = 10; try { start = Integer.parseInt(request.getParameter("page.start")); count = Integer.parseInt(request.getParameter("page.count")); } catch (Exception e) { } Page page = new Page(start, count); List students = studentService.list(page.getStart(), page.getCount()); int total = studentService.getTotal(); page.setTotal(total); request.setAttribute("students", students); request.setAttribute("page", page); return "listStudent"; } @RequestMapping("/addStudent") public String addStudent(HttpServletRequest request, HttpServletResponse response) { Student student = new Student(); int studentID = Integer.parseInt(request.getParameter("studentId")); String name = request.getParameter("name"); int age = Integer.parseInt(request.getParameter("age")); String sex = request.getParameter("sex"); Date birthday = null; try { birthday = Const.DATE_FORMAT.parse(request.getParameter("birthday")); } catch (ParseException e) { logger.error(e.getMessage()); } student.setStudentId(studentID); student.setName(name); student.setAge(age); student.setSex(sex); student.setBirthday(birthday); studentService.addStudent(student); return "redirect:listStudent"; } @RequestMapping("/deleteStudent") public String deleteStudent(int id) { studentService.deleteStudent(id); return "redirect:listStudent"; } @RequestMapping("/editStudent") public ModelAndView editStudent(int id) { ModelAndView mav = new ModelAndView("editStudent"); Student student = studentService.getStudent(id); mav.addObject("student", student); return mav; } @RequestMapping("/showAddStudent") public ModelAndView showAddStudent() { ModelAndView mav = new ModelAndView("showAddStudent"); return mav; } @RequestMapping("/updateStudent") public String updateStudent(HttpServletRequest request, HttpServletResponse response) { Student student = new Student(); int id = Integer.parseInt(request.getParameter("id")); int studentId = Integer.parseInt(request.getParameter("studentId")); String name = request.getParameter("name"); int age = Integer.parseInt(request.getParameter("age")); String sex = request.getParameter("sex"); Date birthday = null; try { birthday = Const.DATE_FORMAT.parse(request.getParameter("birthday")); } catch (ParseException e) { e.printStackTrace(); } student.setId(id); student.setStudentId(studentId); student.setName(name); student.setAge(age); student.setSex(sex); student.setBirthday(birthday); studentService.updateStudent(student); return "redirect:listStudent"; } } ``` 编辑【listStudent.jsp】 ```html <%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%-- 引入JQ和Bootstrap --%> 学生管理系统 - 首页 学生列表 - 共${page.total}人 学号 姓名 年龄 性别 出生日期 编辑 删除 ${s.studentId} ${s.name} ${s.age} ${s.sex} ${s.birthday} class="disabled"> « class="disabled"> ‹ class="disabled"> class="current" >${status.count} class="disabled"> › class="disabled"> » ``` 编辑【editStudent.jsp】 ```html <%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%-- 引入JQ和Bootstrap --%> 学生管理系统 - 编辑页面 编辑学生 学号: 姓名: 年龄: 性别: checked class="radio radio-inline" name="sex" value="男"> 男 checked class="radio radio-inline" name="sex" value="女"> 女 出生日期: 提 交 ``` 编辑【showAddStudent.jsp】 ```html <%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%-- 引入JQ和Bootstrap --%> 学生管理系统 - 新增页面 增加学生 学号: 姓名: 年龄: 性别: 男 女 出生日期: 提 交 ``` # 项目总结 1. 相较于之前用 Servlet + JSP 来完成,很明显的感觉是DAO层的编写方便了很多,仅仅需要编写一个 xml 映射文件和一个 Dao层接口就可以完成功能,而不用自己再去重复的去在每一个 CRUD 方法中去处理结果集,重复而且繁琐。 2. 注解真的很方便,有助于提升开发效率。 3. SSM开发效率快,而且低耦合。基于 xml 配置了大部分的工作,在基于 SSM 框架开发时,可以把专注点集中在逻辑处理上。 4. 在 SSM 框架中能方便的对项目进行修改,这不仅仅得益于框架的约定,使得代码分离并且可复用,也得益于 Maven 工具对于项目的管理。 5. 能够通过一个 Controller 来完成五个 Servlet 的功能,并且通过注解来完成配置。 # 参考资料 1. [学生管理系统(SSM简易版)总结](https://www.jianshu.com/p/6a594fbea51d)