技术准备
为了完成这个项目,需要掌握如下技术:
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 包 :
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 :
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">
学生管理系统
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 的配置:
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">
在【spring-mvc.xml】中完成 Spring MVC 的相关配置:
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">
在【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】中完成日志输出的相关配置:
%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 类:
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 list(int start, int count);
}
然后在【resources/mapper】下创建好对应的映射文件【StudengMapper.xml】:
/p>
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
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类:
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 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 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 list(int start, int count) {
List 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 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】
pageEncoding="UTF-8" %>
学生管理系统 - 首页function del() {
var msg = "您真的确定要删除吗?\n\n请确认!";
if (confirm(msg) == true) {
return true;
} else {
return false;
}
}
学号姓名年龄性别出生日期编辑删除
${s.studentId}${s.name}${s.age}${s.sex}${s.birthday}class="glyphicon glyphicon-trash">
«
‹
href="?page.start=${status.index*page.count}"
class="current"
>${status.count}
›
»
编辑【editStudent.jsp】
pageEncoding="UTF-8" %>
学生管理系统 - 编辑页面$(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;
}
学号: | placeholder="请在这里输入学号"> |
姓名: | |
年龄: | |
性别: | checked class="radio radio-inline" name="sex" value="男"> 男 checked class="radio radio-inline" name="sex" value="女"> 女 |
出生日期: | placeholder="请在这里输入出生日期"> |
提 交 |
编辑【showAddStudent.jsp】
pageEncoding="UTF-8" %>
学生管理系统 - 新增页面$(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;
}
学号: | |
姓名: | |
年龄: | |
性别: | 男 女 |
出生日期: | |
提 交 |
项目总结
相较于之前用 Servlet + JSP 来完成,很明显的感觉是DAO层的编写方便了很多,仅仅需要编写一个 xml 映射文件和一个 Dao层接口就可以完成功能,而不用自己再去重复的去在每一个 CRUD 方法中去处理结果集,重复而且繁琐。
注解真的很方便,有助于提升开发效率。
SSM开发效率快,而且低耦合。基于 xml 配置了大部分的工作,在基于 SSM 框架开发时,可以把专注点集中在逻辑处理上。
在 SSM 框架中能方便的对项目进行修改,这不仅仅得益于框架的约定,使得代码分离并且可复用,也得益于 Maven 工具对于项目的管理。
能够通过一个 Controller 来完成五个 Servlet 的功能,并且通过注解来完成配置。
参考资料