项目实战-SSM简易版学生管理系统

技术准备

为了完成这个项目,需要掌握如下技术:

  • 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 ,便于存取中文数据

DROP DATABASE IF EXISTS student;
CREATE DATABASE student DEFAULT CHARACTER SET utf8;

创建学生表:student

不用学生学号(studentID)作为主键的原因是:不方便操作,例如在更新数据的时候,同时也要更改学号,那这样的操作怎么办呢?
所以我们加了一个 id 用来唯一表示当前数据。

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 下默认区分大小写,因此,数据库名、表明、字段名都不允许出现任何大写字母,避免节外生枝。

原型设计

就是设计界面,在商业项目中,这是很重要的一步,我们可以解除界面原型,低成本、高效率的与客户达成需求的一致性。

这个项目一共就分为四个页面:

  • 主页面


    homepage.JPG
  • 新增学生信息页面


    新增学生页面.JPG
  • 编译学生信息页面
编辑学生信息页面.JPG
  • 学生信息列表展示页面
学生列表展示页面.JPG

SSM 环境搭建

在真正开始编写代码之前,我们首先需要先来搭建好我们的 SSM 环境。
第一步:创建 Maven webapp 项目
首先新建工程,选择 Maven 标签,然后勾选上【Create from archetype】选择 webapp:

7896890-37bca8243a63a411.png

点击下一步,填写上【GroupId】和【ArtifactId】:

截图2018123101114.png
  • GroupId:项目组织唯一的标识符,实际对应 JAVA 的包的结构,也就是 main 目录下 java 的目录结构(包)。
  • AritifactId:项目的唯一标识符,实际对应项目的名称,就是项目根目录的名称。

然后是确认项目路径,可以看到 Maven 配置中的参数,不需要做改动,直接下一步就可以:

截图2018123102858.png

确认项目名称和路径,点击【Finish】即可:

等待一会儿,控制台就会有创建成功的提示信息,我们把【Enable Auto-Import】点上,这个提示会在每次 pom.xml 有改动时出现,自动导入,省掉麻烦:

7896890-2ebc5e0cd0503ad7.png

第二步:搭建项目目录结构
下面就是 Maven 风格的 webapp 的默认目录结构:

7896890-4ac356da8a41ee5b.png

注意: webapp 是默认没有 java 源文件也没有 test 目录的。

遵循 Maven 的统一项目结构,我们搭建出项目的完整目录结构如下图:

StudentManager工程目录结构.png

我们并没有使用 Log4j 来输出日志,而是使用 logback 日志框架。

提示:我们可以在 IDEA 中右键目录然后选择【Make Directory as】,让 IDEA 识别不同的目录作用

这里的目录建好之后还需要设置一下,让 IDEA 识别目录作用,选择【File】>【Project Structure】:

截图201812310252.png

设置好之后点击 OK,即完成了项目目录的搭建。

截图201812310240.png

第三步:配置文件内容
在【pom.xml】文件中声明依赖的 jar 包 :

<?xml version="1.0" encoding="UTF-8"?>
<!--
  Licensed to the Apache Software Foundation (ASF) under one
  or more contributor license agreements.  See the NOTICE file
  distributed with this work for additional information
  regarding copyright ownership.  The ASF licenses this file
  to you under the Apache License, Version 2.0 (the
  "License"); you may not use this file except in compliance
  with the License.  You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing,
  software distributed under the License is distributed on an
  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  KIND, either express or implied.  See the License for the
  specific language governing permissions and limitations
  under the License.
-->
<!-- $Id: pom.xml 642118 2008-03-28 08:04:16Z reinhard $ -->
<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/maven-v4_0_0.xsd">

  <modelVersion>4.0.0</modelVersion>
  <packaging>war</packaging>

  <name>StudentManager</name>
  <groupId>con.netease.cloud</groupId>
  <artifactId>StudentManager</artifactId>
  <version>1.0-SNAPSHOT</version>

  <!--定义变量-->
  <properties>
    <!-- 设置项目编码编码 -->
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <!-- spring版本号 -->
    <spring.version>4.3.5.RELEASE</spring.version>
    <!-- mybatis版本号 -->
    <mybatis.version>3.4.1</mybatis.version>
  </properties>

  <build>
    <finalName>StudentManager</finalName>
    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
      <plugins>
        <plugin>
          <artifactId>maven-clean-plugin</artifactId>
          <version>3.0.0</version>
        </plugin>
        <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
        <plugin>
          <artifactId>maven-resources-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.7.0</version>
          <configuration>
            <source>${maven.compiler.source}</source>
            <target>${maven.compiler.target}</target>
            <encoding>${project.build.sourceEncoding}</encoding>
          </configuration>
        </plugin>
        <plugin>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>2.20.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-war-plugin</artifactId>
          <version>3.2.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-install-plugin</artifactId>
          <version>2.5.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-deploy-plugin</artifactId>
          <version>2.8.2</version>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>

  <dependencies>
    <!-- jstl标签 -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
    </dependency>
    <dependency>
      <groupId>org.apache.taglibs</groupId>
      <artifactId>taglibs-standard-impl</artifactId>
      <version>1.2.5</version>
    </dependency>

    <!-- java ee -->
    <dependency>
      <groupId>javax</groupId>
      <artifactId>javaee-api</artifactId>
      <version>7.0</version>
    </dependency>

    <!-- 单元测试 -->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
    </dependency>

    <!-- 实现slf4j接口并整合 -->
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>1.2.2</version>
    </dependency>

    <!-- JSON -->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.8.7</version>
    </dependency>

    <!-- 数据库 -->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.41</version>
      <scope>runtime</scope>
    </dependency>

    <!-- 数据库连接池 -->
    <dependency>
      <groupId>com.mchange</groupId>
      <artifactId>c3p0</artifactId>
      <version>0.9.5.2</version>
    </dependency>

    <!-- MyBatis -->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>${mybatis.version}</version>
    </dependency>

    <!-- mybatis/spring整合包 -->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>1.3.1</version>
    </dependency>

    <!-- Spring -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>${spring.version}</version>
    </dependency>
  </dependencies>

</project>

<build> 标签是默认生成的
我们在 <properties> 中声明了编码格式以及使用的 spring 和 mybatis 的版本号,然后在 <dependencies> 中声明了具体的 jar 包

【web.xml】中声明编码过滤器并配置 DispatcherServlet :

<?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_3_1.xsd"
         version="3.1">

    <display-name>学生管理系统</display-name>

    <!--首页文件列表-->
    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>

    <!--解决中文乱码问题,一定要放在其他所有过滤器之前-->
    <filter>
        <filter-name>SetCharacterEncoding</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>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>SetCharacterEncoding</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!-- 配置DispatcherServlet -->
    <servlet>
        <servlet-name>SpringMVC</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--将spring文件夹下的spring相关的配置文件整合到一起-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring/spring-*.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
        <async-supported>true</async-supported>
    </servlet>
    <servlet-mapping>
        <servlet-name>SpringMVC</servlet-name>
        <!--默认匹配所有请求-->
        <url-pattern>/</url-pattern>
    </servlet-mapping>

</web-app>

【spring-mybatis.xml】中完成 spring 和 mybatis 的配置:

<?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:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!-- 配置数据库相关参数properties的属性:${url} -->
    <context:property-placeholder location="classpath:jdbc.properties"/>

    <!-- 数据库连接池 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}"/>
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="user" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
        <property name="maxPoolSize" value="${c3p0.maxPoolSize}"/>
        <property name="minPoolSize" value="${c3p0.minPoolSize}"/>
        <property name="autoCommitOnClose" value="${c3p0.autoCommitOnClose}"/>
        <property name="checkoutTimeout" value="${c3p0.checkoutTimeout}"/>
        <property name="acquireRetryAttempts" value="${c3p0.acquireRetryAttempts}"/>
    </bean>

    <!-- 配置SqlSessionFactory对象 -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- 注入数据库连接池 -->
        <property name="dataSource" ref="dataSource"/>
        <!-- 扫描entity包 使用别名 -->
        <property name="typeAliasesPackage" value="com.netease.cloud.entity"/>
        <!-- 扫描sql配置文件:mapper需要的xml文件 -->
        <property name="mapperLocations" value="classpath:mapper/StudentMapper.xml"/>
    </bean>

    <!-- 配置事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- 注入数据库连接池 -->
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- 配置基于注解的声明式事务 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>

    <!-- 配置扫描Dao接口包,动态实现Dao接口,注入到spring容器中 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!-- 注入sqlSessionFactory -->
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
        <!-- 给出需要扫描Dao接口包 -->
        <property name="basePackage" value="com.netease.cloud.dao"/>
    </bean>

    <!-- 扫描dao包下所有使用注解的类型 -->
    <context:component-scan base-package="com.netease.cloud.dao"/>

    <!-- 扫描service包下所有使用注解的类型 -->
    <context:component-scan base-package="com.netease.cloud.service"/>

</beans>

【spring-mvc.xml】中完成 Spring MVC 的相关配置:

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

    <!-- 扫描web相关的bean -->
    <context:component-scan base-package="com.netease.cloud.controller"/>

    <!-- 开启SpringMVC注解模式 -->
    <mvc:annotation-driven/>

    <!-- 静态资源默认servlet配置 -->
    <mvc:default-servlet-handler/>

    <!-- 配置jsp 显示ViewResolver -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
        <property name="prefix" value="/WEB-INF/views/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

</beans>

【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 version="1.0" encoding="UTF-8"?>
<configuration debug="true">
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>
    <root level="debug">
        <appender-ref ref="STDOUT"/>
    </root>
</configuration>

以上就完成了 SSM 框架的基本配置:

  • 添加进了 SSM 项目所需要的 jar 包
  • 配置好了 spring/mybatis/spring MVC 的相关配置信息(自动扫描 com.netease.cloud包下的带有注解的类)
  • 通过 xml 配置的方式配置好了日志和数据库

entity的设计

实体类仅仅是对数据库中表的一一映射(表中字段名应该和实体类中的名称一一对应),同时可能还需要兼顾对业务能力的支持。

在 Packge【com.netease.cloud.entity】下创建 Student 类:

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】接口:

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<Student> list(int start, int count);

}

然后在【resources/mapper】下创建好对应的映射文件【StudengMapper.xml】:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!-- 将namespace的值设置为DAO类对应的路径 -->
<mapper namespace="com.netease.cloud.dao.StudentDao">

    <resultMap id="BaseResultMap" type="Student">
        <id column="id" jdbcType="INTEGER" property="id"/>
        <result column="student_id" jdbcType="INTEGER" property="studentId"/>
        <result column="name" jdbcType="VARCHAR" property="name"/>
        <result column="age" jdbcType="INTEGER" property="age"/>
        <result column="sex" jdbcType="VARCHAR" property="sex"/>
        <result column="birthday" jdbcType="TIMESTAMP" property="birthday"/>
    </resultMap>

    <sql id="Base_Column_List">
        id,student_id,name,age,sex,birthday
    </sql>

    <sql id="Base_Table_Name">
        student
    </sql>

    <!-- 查询一条数据 -->
    <select id="getStudent" resultMap="BaseResultMap" parameterType="java.lang.Integer">
        SELECT
        <include refid="Base_Column_List"/>
        FROM
        <include refid="Base_Table_Name"/>
        WHERE id = #{id,jdbcType=INTEGER}
    </select>

    <!-- 分页查询:查询从start位置开始的count条数据-->
    <select id="list" resultMap="BaseResultMap">
        SELECT
        <include refid="Base_Column_List"/>
        FROM
        <include refid="Base_Table_Name"/>
        ORDER BY student_id DESC
        limit #{param1}, #{param2}
    </select>

    <!-- 查询数据条目 -->
    <select id="getTotal" resultType="java.lang.Integer">
        SELECT COUNT(*) FROM
        <include refid="Base_Table_Name"/>
    </select>

    <!-- 增加一条数据 -->
    <insert id="addStudent" useGeneratedKeys="true" keyColumn="id" parameterType="Student">
        INSERT INTO
        <include refid="Base_Table_Name"/>
        (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})
    </insert>

    <!-- 删除一条数据 -->
    <delete id="deleteStudent" parameterType="java.lang.Integer">
        DELETE FROM
        <include refid="Base_Table_Name"/>
        WHERE id = #{id,jdbcType=INTEGER}
    </delete>

    <!-- 更新一条数据 -->
    <update id="updateStudent" parameterType="Student">
        UPDATE
        <include refid="Base_Table_Name"/>
        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}
    </update>
</mapper>

编写好了 Dao 类是需要测试的,编写BaseTest和StudentDaoTest类:

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 {
}
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<Student> studentList = studentDao.list(0, 1);
        assertNotSame(0, studentList.size());
    }

}

service的设计

问题: 为什么不直接使用 Dao 类而是还要在上面封装一层 Service 层呢?

回答:基于责任分离的原则,Dao 层就应该专注于对数据库的操作,而在 Service 层我们可以增加一些非 CRUD 的方法去更好的完成本身抽离出来的 service 服务(业务处理)。

在【com.netease.cloud.service】包下创建【StudentService】接口:

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<Student> list(int start, int count);
}

并在相同包名下新建一个文件夹【impl】并在该文件夹下创建实现类【StudentServiceImpl】实现接口【StudentService】:

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<Student> list(int start, int count) {
        List<Student> studentList = studentDao.list(start, count);
        if (studentList == null) {
            studentList = Collections.emptyList();
        }
        return studentList;
    }
}

功能开发

在【com.netease.cloud.controller】包下创建【StudentController】控制器:

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<Student> 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】

<!DOCTYPE html>
<%@ page contentType="text/html;charset=UTF-8" language="java"
         pageEncoding="UTF-8" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

<html>
<head>
    <%-- 引入JQ和Bootstrap --%>
    <script src="js/jquery/2.0.0/jquery.min.js"></script>
    <link href="css/bootstrap/3.3.6/bootstrap.min.css" rel="stylesheet">
    <script src="js/bootstrap/3.3.6/bootstrap.min.js"></script>
    <link href="css/style.css" rel="stylesheet">

    <title>学生管理系统 - 首页</title>

    <script>
        function del() {
            var msg = "您真的确定要删除吗?\n\n请确认!";
            if (confirm(msg) == true) {
                return true;
            } else {
                return false;
            }
        }
    </script>
</head>

<body>
    <!--表格展示视图-->
    <div class="listDIV">
    <table class="table table-striped table-bordered table-hover table-condensed">
        <caption>学生列表 - 共${page.total}人</caption>
        <thead>
        <tr class="success">
            <th>学号</th>
            <th>姓名</th>
            <th>年龄</th>
            <th>性别</th>
            <th>出生日期</th>
            <th>编辑</th>
            <th>删除</th>
        </tr>
        </thead>

        <tbody>
        <c:forEach items="${students}" var="s" varStatus="status">
            <tr>
                <td>${s.studentId}</td>
                <td>${s.name}</td>
                <td>${s.age}</td>
                <td>${s.sex}</td>
                <td>${s.birthday}</td>

                <td><a href="/editStudent?id=${s.id}"><span class="glyphicon glyphicon-edit"></span> </a></td>
                <td><a href="/deleteStudent?id=${s.id}" onclick="javascript:return del();"><span
                        class="glyphicon glyphicon-trash"></span> </a></td>
            </tr>
        </c:forEach>
        </tbody>
    </table>
</div>

    <!--分页展示控件-->
    <nav class="pageDIV">
        <ul class="pagination">
            <li <c:if test="${!page.hasPreviouse}">class="disabled"</c:if>>
                <a href="?page.start=0">
                    <span>«</span>
                </a>
            </li>

            <li <c:if test="${!page.hasPreviouse}">class="disabled"</c:if>>
                <a href="?page.start=${page.start-page.count}">
                    <span>‹</span>
                </a>
            </li>

            <c:forEach begin="0" end="${page.totalPage-1}" varStatus="status">

                <c:if test="${status.count*page.count-page.start<=30 && status.count*page.count-page.start>=-10}">
                    <li <c:if test="${status.index*page.count==page.start}">class="disabled"</c:if>>
                        <a
                                href="?page.start=${status.index*page.count}"
                                <c:if test="${status.index*page.count==page.start}">class="current"</c:if>
                        >${status.count}</a>
                    </li>
                </c:if>
            </c:forEach>

            <li <c:if test="${!page.hasNext}">class="disabled"</c:if>>
                <a href="?page.start=${page.start+page.count}">
                    <span>›</span>
                </a>
            </li>
            <li <c:if test="${!page.hasNext}">class="disabled"</c:if>>
                <a href="?page.start=${page.last}">
                    <span>»</span>
                </a>
            </li>
        </ul>
    </nav>
</body>
</html>

编辑【editStudent.jsp】

<!DOCTYPE html>
<%@ page contentType="text/html;charset=UTF-8" language="java"
         pageEncoding="UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<html>
<head>

    <%-- 引入JQ和Bootstrap --%>
    <script src="js/jquery/2.0.0/jquery.min.js"></script>
    <link href="css/bootstrap/3.3.6/bootstrap.min.css" rel="stylesheet">
    <script src="js/bootstrap/3.3.6/bootstrap.min.js"></script>
    <link href="css/style.css" rel="stylesheet">

    <title>学生管理系统 - 编辑页面</title>

    <script>
        $(function () {
            $("#editForm").submit(function () {
                if (!checkEmpty("studentId", "学号"))
                    return false;
                if (!checkEmpty("name", "姓名"))
                    return false;
                if (!checkEmpty("age", "年龄"))
                    return false;
                return true;
            });
        });
        function checkEmpty(id, name) {
            var value = $("#" + id).val();
            if (value.length == 0) {
                alert(name + "不能为空");
                $("#" + id).focus();
                return false;
            }
            return true;
        }
    </script>
</head>

<body>

<div class="editDIV">

    <div class="panel panel-success">
        <div class="panel-heading">
            <h3 class="panel-title">编辑学生</h3>
        </div>
        <div class="panel-body">

            <form method="post" id="editForm" action="/updateStudent" role="form">
                <table class="editTable">
                    <tr>
                        <td>学号:</td>
                        <td><input type="text" name="studentId" id="studentId" value="${student.studentId}"
                                   placeholder="请在这里输入学号"></td>
                    </tr>
                    <tr>
                        <td>姓名:</td>
                        <td><input type="text" name="name" id="name" value="${student.name}" placeholder="请在这里输入名字">
                        </td>
                    </tr>
                    <tr>
                        <td>年龄:</td>
                        <td><input type="text" name="age" id="age" value="${student.age}" placeholder="请在这里输入年龄"></td>
                    </tr>
                    <tr>
                        <td>性别:</td>
                        <td><input type="radio" <c:if test="${student.sex == '男'}">checked</c:if> class="radio radio-inline" name="sex" value="男"> 男
                            <input type="radio" <c:if test="${student.sex == '女'}">checked</c:if> class="radio radio-inline" name="sex" value="女"> 女
                        </td>
                    </tr>
                    <tr>
                        <td>出生日期:</td>
                        <td><input type="date" name="birthday" id="birthday" value="${student.birthday}"
                                   placeholder="请在这里输入出生日期"></td>
                    </tr>
                    <tr class="submitTR">
                        <td colspan="2" align="center">
                            <input type="hidden" name="id" value="${student.id}">
                            <button type="submit" class="btn btn-success">提 交</button>
                        </td>

                    </tr>

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

</div>

</body>
</html>

编辑【showAddStudent.jsp】

<!DOCTYPE html>
<%@ page contentType="text/html;charset=UTF-8" language="java"
         pageEncoding="UTF-8" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

<html>
<head>
    <%-- 引入JQ和Bootstrap --%>
    <script src="js/jquery/2.0.0/jquery.min.js"></script>
    <link href="css/bootstrap/3.3.6/bootstrap.min.css" rel="stylesheet">
    <script src="js/bootstrap/3.3.6/bootstrap.min.js"></script>
    <link href="css/style.css" rel="stylesheet">

    <title>学生管理系统 - 新增页面</title>

    <script>
        $(function () {
            $("ul.pagination li.disabled a").click(function () {
                return false;
            });
            $("#addForm").submit(function () {
                if (!checkEmpty("studentId", "学号"))
                    return false;
                if (!checkEmpty("name", "姓名"))
                    return false;
                if (!checkEmpty("age", "年龄"))
                    return false;
                return true;
            });
        });

        function checkEmpty(id, name) {
            var value = $("#" + id).val();
            if (value.length == 0) {
                alert(name + "不能为空");
                $("#" + id).focus();
                return false;
            }
            return true;
        }
    </script>
</head>

<body>

<!--新增学生视图-->
<div class="addDIV">
    <div class="panel panel-success">
        <div class="panel-heading">
            <h3 class="panel-title">增加学生</h3>
        </div>
        <div class="panel-body">
            <form method="post" id="addForm" action="/addStudent" role="form">
                <table class="addTable">
                    <tr>
                        <td>学号:</td>
                        <td><input type="text" name="studentId" id="studentId" placeholder="请在这里输入学号"></td>
                    </tr>
                    <tr>
                        <td>姓名:</td>
                        <td><input type="text" name="name" id="name" placeholder="请在这里输入名字"></td>
                    </tr>
                    <tr>
                        <td>年龄:</td>
                        <td><input type="text" name="age" id="age" placeholder="请在这里输入年龄"></td>
                    </tr>
                    <tr>
                        <td>性别:</td>
                        <td><input type="radio" checked class="radio radio-inline" name="sex" value="男"> 男
                            <input type="radio" class="radio radio-inline" name="sex" value="女"> 女
                        </td>
                    </tr>
                    <tr>
                        <td>出生日期:</td>
                        <td><input type="date" name="birthday" id="birthday" placeholder="请在这里输入出生日期"></td>
                    </tr>
                    <tr class="submitTR">
                        <td colspan="2" align="center">
                            <button type="submit" class="btn btn-success">提 交</button>
                        </td>
                    </tr>
                </table>
            </form>
        </div>
    </div>
</div>


</body>
</html>

项目总结

  1. 相较于之前用 Servlet + JSP 来完成,很明显的感觉是DAO层的编写方便了很多,仅仅需要编写一个 xml 映射文件和一个 Dao层接口就可以完成功能,而不用自己再去重复的去在每一个 CRUD 方法中去处理结果集,重复而且繁琐。
  2. 注解真的很方便,有助于提升开发效率。
  3. SSM开发效率快,而且低耦合。基于 xml 配置了大部分的工作,在基于 SSM 框架开发时,可以把专注点集中在逻辑处理上。
  4. 在 SSM 框架中能方便的对项目进行修改,这不仅仅得益于框架的约定,使得代码分离并且可复用,也得益于 Maven 工具对于项目的管理。
  5. 能够通过一个 Controller 来完成五个 Servlet 的功能,并且通过注解来完成配置。

参考资料

  1. 学生管理系统(SSM简易版)总结
  2. 项目源码
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352

推荐阅读更多精彩内容