图书管理系统

  • 开发环境与技术栈
  • 项目功能
  • 项目演示
    • 1、注册和登录页面
    • 2、借阅信息管理页面
    • 3、新增借阅信息
    • 4、修改借阅信息
    • 5、删除借阅信息
  • 开发准备
  • 数据库设计
    • 1、数据库表关系说明
    • 2、创建数据库及数据库表
  • 前后端接口约定
    • 1、查询专业字典
    • 2、查询毕业年份字典
    • 3、查询图书借阅信息
    • 4、查询班级(数据字典)
    • 5、查询图书(数据字典)
    • 6、查询学生(数据字典)
    • 7、新增图书借阅信息
    • 8、删除图书借阅信息
    • 9、查询图书借阅详情
    • 10、修改图书借阅信息
  • 代码设计
    • 1、设计数据库实体类
    • 2、设计工具类
    • 3、自定义异常类
    • 4、设计dao层
    • 5、设计API接口
  • 项目代码结构

开发环境与技术栈

  • Windows/Mac/Linux
  • Maven
  • Servlet
  • MySQL
  • Jackson

项目功能

主要业务:管理学校图书信息,主要是用来管理学生借阅图书的信息。

  • 用户注册(打开主页,可以看到注册按钮);
  • 用户登录(打开主页,可以看到登录界面);
  • 新增图书借阅信息;
  • 修改图书借阅信息;
  • 删除图书借阅信息。

项目演示

1、注册和登录页面

2、借阅信息管理页面

3、新增借阅信息

4、修改借阅信息

5、删除借阅信息

开发准备

1.IDEA编码配置:

2.自动编码设置:

3.配置好pom.xml文件,指定打包方式,引入相关依赖等等:

<!-- 比如引入MySQL数据库JDBC驱动包 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.38</version></dependency>

4.生成项目的web.xml文件:

5.在IDEA中设置好tomcat部署项目自动发布 功能:

6.准备好前端资源

数据库设计

1、数据库表关系说明


从上面的关系图可以很清楚的看出数据库中主要的几个表之间的关系:

  • 学生表和班级表为多对一关系;
  • 学生表和图书借阅信息表为一对多关系;
  • 图书表和图书借阅信息表为一对多关系;
  • 由2和3可知,图书表和学生表在借阅的场景下,表现出多对多关系。

2、创建数据库及数据库表

drop database if exists book;
create database book character set utf8mb4;use book;-- 创建用户表
drop table if exists user;
create table user(id int primary key auto_increment,username varchar(20) not null unique comment '用户账号',password varchar(20) not null comment '密码',nickname varchar(20) comment '用户昵称',email varchar(50) comment '邮箱',create_time timestamp default NOW() comment '创建时间'
) comment '用户表';-- 创建学生表
drop table if exists student;
create table student(id int primary key auto_increment,student_name varchar(20) not null comment '姓名',student_no varchar(20) comment '学号',id_card varchar(20) comment '身份证号',student_email varchar(50) comment '邮箱',classes_id int comment '班级id',create_time timestamp default NOW() comment '创建时间',foreign key (classes_id) references classes(id)
) comment '学生表';-- 创建班级表
drop table if exists classes;
create table classes(id int primary key auto_increment,classes_name varchar(20) not null comment '班级名称',classes_graduate_year varchar(20) comment '毕业年份,数据字典000001',classes_major varchar(20) comment '专业,数据字典000002',classes_desc varchar(50) comment '备注',create_time timestamp default NOW() comment '创建时间'
) comment '班级表';-- 创建图书表
drop table if exists book;
create table book(id int primary key auto_increment,book_name varchar(50) not null comment '图书名称',author varchar(20) comment '作者',price decimal(10,2) comment '价格',create_time timestamp default NOW() comment '创建时间'
) comment '图书信息';--创建借阅信息表
drop table if exists borrow_record;
create table borrow_record(id int primary key auto_increment,book_id int comment '图书id',student_id int comment '学生id',start_time timestamp not null comment '借阅日期',end_time timestamp null default null comment '归还日期',create_time timestamp default NOW() comment '创建时间',foreign key (book_id) references book(id),foreign key (student_id) references student(id)
) comment '图书借阅信息';-- 为了后期程序良好的拓展性,增加两个表,一个是数据字典表,一个是数据字典标签表
drop table if exists dictionary;
create table dictionary(id int primary key auto_increment,dictionary_key varchar(20) not null unique comment '键',dictionary_value varchar(20) not null comment '值',dictionary_desc varchar(20) comment '备注',create_time timestamp default NOW() comment '创建时间'
) comment '数据字典';drop table if exists dictionary_tag;
create table dictionary_tag(id int primary key auto_increment,dictionary_tag_key varchar(20) not null comment '键',dictionary_tag_value varchar(20) not null comment '值',dictionary_tag_desc varchar(20) comment '备注',dictionary_id int comment '数据字典id',create_time timestamp default NOW() comment '创建时间',foreign key (dictionary_id) references dictionary(id)
) comment '数据字典标签';

前后端接口约定

要实现功能,需要先明确前后端需要约定好的接口。接口的定义一般是前后端约定好的,所以也和前端代码息息相关,前端需要什么数据,后端需要什么数据,数据的格式等等,也都会在接口中体现。

1、查询专业字典

(1)请求

GET dict/tag/query?dictionaryKey=000002    //请求方法和路径以及请求参数

(2)响应

//响应格式样例
{"success" : true,"code" : "200","message" : "操作成功","data" : [ {"dictionaryTagKey" : "000002001","dictionaryTagValue" : "中文系"}, {"dictionaryTagKey" : "000002002","dictionaryTagValue" : "英语系"}, {"dictionaryTagKey" : "000002003","dictionaryTagValue" : "计算机科学与技术"} ]
}

2、查询毕业年份字典

(1)请求

GET dict/tag/query?dictionaryKey=000001   //请求方法和路径以及请求参数

(2)响应

//响应格式样例
{"success" : true,"code" : "200","message" : "操作成功","data" : [ {"dictionaryTagKey" : "000001001","dictionaryTagValue" : "2020届"}, {"dictionaryTagKey" : "000001002","dictionaryTagValue" : "2021届"}, {"dictionaryTagKey" : "000001003","dictionaryTagValue" : "2022届"}, {"dictionaryTagKey" : "000001004","dictionaryTagValue" : "2023届"} ]
}

3、查询图书借阅信息

(1)请求

GET borrowRecord/query?searchText=&sortOrder=asc&pageSize=7&pageNumber=1

(2)响应

{"success" : true,"code" : "200","message" : "操作成功","total" : 9,"data" : [ {"id" : 1,"startTime" : "2020-01-01 14:20:00","endTime" : "2020-02-01 14:20:00","createTime" : "2020-06-17 15:52:33","book" : {"id" : 1,"bookName" : "高等数学","author" : "邱伯驺","price" : 115.20},"classes" : {"id" : 1,"classesName" : "计科三班","classesGraduateYear" : "000001001","classesMajor" : "000002002","classesDesc" : "计算机科学与技术三班"},"student" : {"id" : 1,"studentName" : "亚索","studentNo" : "s00001","idCard" : "222222222222222222","studentEmail" : "123@qq.com"}}, {"id" : 7,"startTime" : "2020-02-01 14:20:00","endTime" : "2020-02-21 14:20:00","createTime" : "2020-06-17 15:52:33","book" : {"id" : 3,"bookName" : "数据结构","author" : "严蔚敏","price" : 33.00},"classes" : {"id" : 1,"classesName" : "计科三班","classesGraduateYear" : "000001001","classesMajor" : "000002002","classesDesc" : "计算机科学与技术三班"},"student" : {"id" : 3,"studentName" : "阿卡丽","studentNo" : "s00003","idCard" : "222222222222222224","studentEmail" : "123@qq.com"}} ]
}

4、查询班级(数据字典)

使用在下拉菜单中,要求按照数据字典响应格式返回。
(1)请求

GET http://localhost:8080/classes/queryAsDict

(2)响应

{"success" : true,"code" : "200","message" : "操作成功","data" : [ {"dictionaryTagKey" : "1","dictionaryTagValue" : "计科一班","classesGraduateYear" : "000001001","classesMajor" : "000002003"}, {"dictionaryTagKey" : "2","dictionaryTagValue" : "计科二班","classesGraduateYear" : "000001002","classesMajor" : "000002003"}, {"dictionaryTagKey" : "3","dictionaryTagValue" : "计科一班","classesGraduateYear" : "000001003","classesMajor" : "000002003"} ]
}

5、查询图书(数据字典)

(1)请求

GET book/queryAsDict

(2)响应

{"success" : true,"code" : "200","message" : "操作成功","data" : [ {"dictionaryTagKey" : "1","dictionaryTagValue" : "高等数学","author" : "   邱伯驺","price" : 115.20}, {"dictionaryTagKey" : "2","dictionaryTagValue" : "数据结构","author" : "严蔚敏","price" : 61.50}, {"dictionaryTagKey" : "3","dictionaryTagValue" : "深入理解Java虚拟机","author" : "周志明","price" : 33.00}, {"dictionaryTagKey" : "4","dictionaryTagValue" : "MySQL必知必会","author" : "Ben Forta","price" : 33.00} ]
}

6、查询学生(数据字典)

级联下拉菜单:选择班级下拉选项之后,根据选择的班级id查询所有学生。要求按照数据字典响应格式返回。
(1)请求

GET student/queryAsDict?dictionaryKey=2

(2)响应

{"success" : true,"code" : "200","message" : "操作成功","data" : [ {"dictionaryTagKey" : "6","dictionaryTagValue" : "剑姬","studentNo" : "s00006","idCard" : "222222222222222227"}, {"dictionaryTagKey" : "7","dictionaryTagValue" : "锐雯","studentNo" : "s00007","idCard" : "222222222222222228"} ]
}

7、新增图书借阅信息

(1)请求

POST borrowRecord/add
Content-Type: application/json{"studentId":"7","bookId":"3","startTime":"2020-06-10 19:40:56","endTime":""}

(2)响应

{"success" : true,"code" : "200","message" : "操作成功"
}

8、删除图书借阅信息

(1)请求

GET borrowRecord/delete?ids=2&ids=3

(2)响应

{"success" : true,"code" : "200","message" : "操作成功"
}

9、查询图书借阅详情

(1)请求

GET borrowRecord/queryById?id=1

(2)响应

{"success" : true,"code" : "200","message" : "操作成功","data" : {"id" : 1,"startTime" : "2020-01-01 14:20:00","endTime" : "2020-02-01 14:20:00","createTime" : "2020-06-17 15:52:33","book" : {"id" : 1,"bookName" : "高等数学","author" : "邱伯驺","price" : 115.20},"classes" : {"id" : 1,"classesName" : "计科三班","classesGraduateYear" : "000001001","classesMajor" : "000002002","classesDesc" : "计算机科学与技术三班"},"student" : {"id" : 1,"studentName" : "亚索","studentNo" : "s00001","idCard" : "222222222222222222","studentEmail" : "123@qq.com"}}
}

10、修改图书借阅信息

(1)请求

POST borrowRecord/update
Content-Type: application/json{"id":"10","studentId":"3","bookId":"4","startTime":"2020-06-11 19:24:46","endTime":"2020-06-18 19:54:49"}

(2)响应

{"success" : true,"code" : "200","message" : "操作成功"
}

有了上述的约定,后面不管是后端代码,还是前端代码都需要严格遵守上述约定。否则,就无法完成交互。

代码设计

1、设计数据库实体类

(1)User实体类

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;import java.util.Date;/*** 用户表*/
@Getter
@Setter
@ToString
public class User {//用户IDprivate Integer id;//用户名private String username;//用户密码private String password;//用户昵称private String nickname;//用户邮箱private String email;//创建时间private Date createTime;
}

(2)Student实体类

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;import java.util.Date;/*** 学生表*/
@Getter
@Setter
@ToString
public class Student extends DictionaryTag{private Integer id;//学生姓名private String studentName;//学号private String studentNo;//身份证号private String idCard;//邮箱private String studentEmail;//班级IDprivate Integer classesId;//创建时间private Date createTime;
}

(3)Class实体类

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;import java.util.Date;/*** 班级表*/
@Getter
@Setter
@ToString
public class Classes extends DictionaryTag{private Integer id;//班级名称private String classesName;//毕业年份,数据字典000001private String classesGraduateYear;//专业,数据字典000002private String classesMajor;//备注private String classesDesc;//创建时间private Date createTime;
}

(4)Book实体类

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;import java.math.BigDecimal;
import java.util.Date;@Getter
@Setter
@ToString
public class Book extends DictionaryTag{//如果用int类型的话,不赋值的话默认为0。因为数据库中又很多0,直接使用int有可能会达不到预期查询结果private Integer id;private String bookName;//作者private String author; //对应数据库中的decimal类型。跟金钱相关的一般都用这个。private BigDecimal price;//创建时间private Date createTime;
}

(5)BorrowRecord实体类

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;import java.util.Date;/*** 图书借阅信息*/
@Getter
@Setter
@ToString
public class BorrowRecord {private Integer id;//图书IDprivate Integer bookId;//学生IDprivate Integer studentId;//借阅日期private Date startTime;//归还日期private Date endTime;//创建时间private Date createTime;//书private Book book;//班级private Classes classes;//学生 private Student student;
}

(6)ResponseResult实体类

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;@Getter
@Setter
@ToString
//http请求返回响应的统一格式,具体需要的字段,是前端后端一起约定好的
public class ResponseResult {private boolean success;  //前端响应状态码为200,但是success可以为falseprivate String code;      //自定义的消息编码private String message;   //自定义的消息内容private Integer total;    //分页需要的字段,查询总的行数(不是分页的数量)private Object data;      //业务数据private String stackTrace; //出现异常时的堆栈信息
}

2、设计工具类

(1)DBUtil工具类(对JDBC操作进行简单的封装)

import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
import liufc.exception.SystemException;import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;public class DBUtil {private static final String URL = "jdbc:mysql://localhost:3306/book";private static final String USERNAME = "用户名";private static final String PASSWORD = "密码";//双重校验锁写单例模式private static volatile DataSource DATA_SOURCE;private DBUtil() {}private static DataSource getDataSource() {if(DATA_SOURCE == null) {synchronized (DBUtil.class) {if(DATA_SOURCE == null) {DATA_SOURCE = new MysqlDataSource();((MysqlDataSource) DATA_SOURCE).setUrl(URL);((MysqlDataSource) DATA_SOURCE).setUser(USERNAME);((MysqlDataSource) DATA_SOURCE).setPassword(PASSWORD);}}}return  DATA_SOURCE;}public static Connection getConnection() {try {return getDataSource().getConnection();} catch (SQLException e) {throw new SystemException("000001","获取数据库连接失败",e);}}public static void close(Connection c, Statement s) {close(c,s,null);   //因为修改和添加不返回结果集,所以,不用释放。可以直接重载下面写好的函数}//释放资源public static void close(Connection c, Statement s, ResultSet r) {try {if(r != null) {r.close();}if(s != null) {s.close();}if(c != null) {c.close();}} catch (SQLException e) {throw new SystemException("000002","释放数据库资源出错",e);}}
}

(2)JSONUtil类

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import liufc.exception.SystemException;import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;public class JSONUtil {private static final ObjectMapper MAPPER;static {MAPPER = new ObjectMapper();//时间格式MAPPER.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));}//读取输入流的json数据,反序列化对象。// 泛型操作:<T>方法上定义泛型类,返回值和传入参数都可以使用泛型。public static <T> T read(InputStream is,Class<T> clazz) {try {return MAPPER.readValue(is,clazz);} catch (IOException e) {throw new SystemException("000003","http请求,解析json数据出错",e);}}public static String write(Object o) {try {return MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(o);} catch (JsonProcessingException e) {throw new SystemException("000004","json序列化对象出错"+o,e);}}
}

3、自定义异常类

(1)基本异常类

import lombok.Getter;
import lombok.Setter;@Getter
@Setter
public class BaseException extends RuntimeException{protected String code;public BaseException(String code, String message) {super(message);this.code = code;}public BaseException(String code, String message, Throwable cause) {super(message, cause);this.code = code;}
}

(2)业务异常类

//业务异常类
public class BusinessException extends BaseException{public BusinessException(String code, String message) {super("BUS" + code, "业务异常:" + message);}public BusinessException(String code, String message, Throwable cause) {super("BUS" + code, "业务异常:" + message, cause);}
}

(3)客户端异常类

//客户端异常类
public class ClientException extends BaseException{public ClientException(String code, String message) {super("CLI" + code, "客户端错误" + message);}public ClientException(String code, String message, Throwable cause) {super("CLI" + code, "客户端错误" + message, cause);}
}

(4)系统异常类

//系统异常类
public class SystemException extends BaseException{public SystemException(String code, String message) {super("SYS" + code, "系统异常:" + message);}public SystemException(String code, String message, Throwable cause) {super("SYS" + code, "系统异常:" + message, cause);}
}

4、设计dao层

dao层是数据访问层,这里设计几个dao类,来封装对数据库表进行增删查改等基础操作的功能。

(1)UserDao(封装针对user表的基本操作)

import liufc.exception.SystemException;
import liufc.model.User;
import liufc.util.DBUtil;import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;public class UserDAO {public static User query(User user) {//查询结果直接通过一个User实体类返回User queryUser = null;Connection c = null;PreparedStatement ps = null;ResultSet rs = null;try {c = DBUtil.getConnection();String sql = "select id,username,password,nickname from user where username=? and password=?";ps = c.prepareStatement(sql);ps.setString(1,user.getUsername());ps.setString(2,user.getPassword());rs = ps.executeQuery();while (rs.next()) {queryUser = user;queryUser.setId(rs.getInt("id"));queryUser.setNickname(rs.getString("nickname"));}}catch (Exception e) {throw new SystemException("00007","用户登录校验数据库查询出错",e);}finally {DBUtil.close(c,ps,rs);}return queryUser;}
}

(2)StudentDao

import liufc.exception.SystemException;
import liufc.model.Student;
import liufc.util.DBUtil;import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;public class StudentDAO {public static List<Student> queryAsDict(int id) {List<Student> students = new ArrayList<>();Connection c = null;PreparedStatement ps = null;ResultSet rs = null;try {c = DBUtil.getConnection();String sql = "select id,student_name,id_card,student_no from student where classes_id=?";ps = c.prepareStatement(sql);ps.setInt(1,id);rs = ps.executeQuery();while (rs.next()) {Student student = new Student();student.setDictionaryTagKey(rs.getString("id"));student.setDictionaryTagValue(rs.getString("student_name"));student.setStudentNo(rs.getString("student_no"));student.setIdCard(rs.getString("id_card"));students.add(student);}}catch (Exception e) {throw new SystemException("00005","查询学生数据字典出错",e);}finally {DBUtil.close(c,ps,rs);}return students;}
}

(3)ClassDao

import liufc.exception.SystemException;
import liufc.model.Classes;
import liufc.util.DBUtil;import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;public class ClassesDAO {public static List<Classes> queryAsDict() {List<Classes> classesList = new ArrayList<>();Connection c = null;PreparedStatement ps = null;ResultSet rs = null;try {c = DBUtil.getConnection();String sql = "select id, classes_name, classes_graduate_year, classes_major from classes";ps = c.prepareStatement(sql);rs = ps.executeQuery();while (rs.next()) {Classes classes = new Classes();classes.setDictionaryTagKey(String.valueOf(rs.getInt("id")));classes.setDictionaryTagValue(rs.getString("classes_name"));classes.setClassesGraduateYear(rs.getString("classes_graduate_year"));classes.setClassesMajor(rs.getString("classes_major"));classesList.add(classes);}}catch (Exception e) {throw new SystemException("00003","查询班级数据字典出错",e);}finally {DBUtil.close(c,ps,rs);}return classesList;}
}

(4)BookDao

import liufc.exception.SystemException;
import liufc.model.Book;
import liufc.util.DBUtil;import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;public class BookDAO {public static List<Book> queryAsDict() {List<Book> books = new ArrayList<>();Connection c = null;PreparedStatement ps = null;ResultSet rs = null;try {c = DBUtil.getConnection();String sql = "select id,book_name,author,price from book";ps = c.prepareStatement(sql);rs = ps.executeQuery();while (rs.next()) {Book book = new Book();//下面两个字段是从id和name来的book.setDictionaryTagKey(String.valueOf(rs.getInt("id")));book.setDictionaryTagValue(rs.getString("book_name"));book.setAuthor(rs.getString("author"));book.setPrice(rs.getBigDecimal("price"));books.add(book);}}catch (Exception e) {throw new SystemException("00004","查询图书数据字典出错",e);}finally {DBUtil.close(c,ps,rs);}return books;}
}

(5)BorrowRecordDAO

import liufc.exception.SystemException;
import liufc.model.*;
import liufc.util.CountHolder;
import liufc.util.DBUtil;import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;public class BorrowRecordDAO {public static List<BorrowRecord> query(Page p) {List<BorrowRecord> records = new ArrayList<>();Connection c = null;PreparedStatement ps = null;ResultSet rs = null;try {c = DBUtil.getConnection();//StringBuilder是因为要根据条件来判断是否选择拼接字符串StringBuilder sql = new StringBuilder("select br.id," +"       br.book_id," +"       br.student_id," +"       br.start_time," +"       br.end_time," +"       br.create_time," +"       b.book_name," +"       b.author," +"       b.price," +"       s.student_name," +"       s.student_no," +"       s.id_card," +"       s.student_email," +"       s.classes_id," +"       c.classes_name," +"       c.classes_graduate_year," +"       c.classes_major," +"       c.classes_desc" +" from borrow_record br" +"         join book b on br.book_id = b.id" +"         join student s on br.student_id = s.id" +"         join classes c on s.classes_id = c.id");//搜索内容不为空字符串,根据学生姓名和图书名称包含搜索内容,就查询出if(p.getSearchText() != null && p.getSearchText().trim().length() > 0){sql.append(" WHERE s.student_name like ? or  b.book_name like ?");}//升序或降序.如果使用占位符替换时,字符串替换会自动添加' ',而这里的desc不能加单引号if(p.getSortOrder() != null && p.getSortOrder().trim().length() > 0) {sql.append("  order by create_time "+p.getSortOrder());}StringBuilder countSQL = new StringBuilder("select count(0) count from (");countSQL.append(sql);countSQL.append(") tmp");//获取查询结果集的数量ps = c.prepareStatement(countSQL.toString());if(p.getSearchText() != null && p.getSearchText().trim().length() > 0){ps.setString(1,"%"+p.getSearchText()+"%");ps.setString(2,"%"+p.getSearchText()+"%");}rs = ps.executeQuery();while (rs.next()) {int count = rs.getInt("count");  //获取查询总数量//需要在返回的数据中,设置到total字段(data业务数据字段同级,这里设置不进去)//保存变量在自己的线程中:ThreadLocalCountHolder.set(count);}//分页查询sql.append(" limit ?,?");ps = c.prepareStatement(sql.toString());int i = 1;if(p.getSearchText() != null && p.getSearchText().trim().length() > 0){ps.setString(i++,"%"+p.getSearchText()+"%");ps.setString(i++,"%"+p.getSearchText()+"%");}//页码转索引:前一页的页码*每页的数量,索引从0开始ps.setInt(i++,(p.getPageNumber()-1)*p.getPageSize());ps.setInt(i++,p.getPageSize());rs = ps.executeQuery();while (rs.next()) {//设置图书借阅信息BorrowRecord br = new BorrowRecord();br.setId(rs.getInt("id"));//得把数据库时间强转为Java时间br.setStartTime(new Date(rs.getTimestamp("start_time").getTime()));br.setEndTime(new Date(rs.getTimestamp("end_time").getTime()));br.setCreateTime(new Date(rs.getTimestamp("create_time").getTime()));//设置图书信息Book book = new Book();book.setId(rs.getInt("book_id"));book.setBookName(rs.getString("book_name"));book.setAuthor(rs.getString("author"));book.setPrice(rs.getBigDecimal("price"));br.setBook(book);//设置学生信息Student s = new Student();s.setId(rs.getInt("student_id"));s.setStudentName(rs.getString("student_name"));s.setStudentNo(rs.getString("student_no")); //学号s.setIdCard(rs.getString("id_card"));s.setStudentEmail(rs.getString("student_email"));br.setStudent(s);//设置班级信息Classes classes = new Classes();classes.setId(rs.getInt("classes_id"));classes.setClassesName(rs.getString("classes_name"));classes.setClassesGraduateYear(rs.getString("classes_graduate_year"));//毕业年份classes.setClassesMajor(rs.getString("classes_major")); //专业classes.setClassesDesc(rs.getString("classes_desc")); //班级描述br.setClasses(classes);records.add(br);}}catch (Exception e) {throw new SystemException("00001","查询图书借阅信息出错",e);}finally {DBUtil.close(c,ps,rs);}return records;}public static BorrowRecord queryById(int id) {//图书借阅信息BorrowRecord br = new BorrowRecord();Connection c = null;PreparedStatement ps = null;ResultSet rs = null;try {c = DBUtil.getConnection();String sql = "select br.id," +"       br.book_id," +"       br.student_id," +"       br.start_time," +"       br.end_time," +"       br.create_time," +"       b.book_name," +"       b.author," +"       b.price," +"       s.student_name," +"       s.student_no," +"       s.id_card," +"       s.student_email," +"       s.classes_id," +"       c.classes_name," +"       c.classes_graduate_year," +"       c.classes_major," +"       c.classes_desc" +" from borrow_record br" +"         join book b on br.book_id = b.id" +"         join student s on br.student_id = s.id" +"         join classes c on s.classes_id = c.id" +" where br.id=?";ps = c.prepareStatement(sql);ps.setInt(1,id);rs = ps.executeQuery();while (rs.next()) {br.setId(rs.getInt("id"));//得把数据库时间强转为Java时间br.setStartTime(new Date(rs.getTimestamp("start_time").getTime()));br.setEndTime(new Date(rs.getTimestamp("end_time").getTime()));br.setCreateTime(new Date(rs.getTimestamp("create_time").getTime()));//设置图书信息Book book = new Book();book.setId(rs.getInt("book_id"));book.setBookName(rs.getString("book_name"));book.setAuthor(rs.getString("author"));book.setPrice(rs.getBigDecimal("price"));br.setBook(book);//设置学生信息Student s = new Student();s.setId(rs.getInt("student_id"));s.setStudentName(rs.getString("student_name"));s.setStudentNo(rs.getString("student_no")); //学号s.setIdCard(rs.getString("id_card"));s.setStudentEmail(rs.getString("student_email"));br.setStudent(s);//设置班级信息Classes classes = new Classes();classes.setId(rs.getInt("classes_id"));classes.setClassesName(rs.getString("classes_name"));classes.setClassesGraduateYear(rs.getString("classes_graduate_year"));//毕业年份classes.setClassesMajor(rs.getString("classes_major")); //专业classes.setClassesDesc(rs.getString("classes_desc")); //班级描述br.setClasses(classes);}}catch (Exception e) {throw new SystemException("00006","查询图书借阅信息详情出错",e);}finally {DBUtil.close(c,ps,rs);}return br;}public static int insert(BorrowRecord record) {Connection c = null;PreparedStatement ps = null;try {c = DBUtil.getConnection();String sql = "insert borrow_record(book_id,student_id,start_time,end_time) values (?,?,?,?)";ps = c.prepareStatement(sql);ps.setInt(1,record.getBookId());ps.setInt(2,record.getStudentId());ps.setTimestamp(3,new Timestamp(record.getStartTime().getTime()));ps.setTimestamp(4,new Timestamp(record.getEndTime().getTime()));return ps.executeUpdate();}catch (Exception e) {throw new SystemException("000010","插入图书借阅信息出错",e);}finally {DBUtil.close(c,ps);}}public static int update(BorrowRecord record) {Connection c = null;PreparedStatement ps = null;try {c = DBUtil.getConnection();String sql = "update borrow_record set book_id=?,student_id=?,start_time=?,end_time=? where id=?";ps = c.prepareStatement(sql);ps.setInt(1,record.getBookId());ps.setInt(2,record.getStudentId());ps.setTimestamp(3,new Timestamp(record.getStartTime().getTime()));ps.setTimestamp(4,new Timestamp(record.getEndTime().getTime()));ps.setInt(5,record.getId());return ps.executeUpdate();}catch (Exception e) {throw new SystemException("000011","修改图书借阅信息出错",e);}finally {DBUtil.close(c,ps);}}public static int delete(String[] ids) {Connection c = null;PreparedStatement ps = null;try {c = DBUtil.getConnection();//因为要修改的行数是一个动态数组,不固定,所以要使用拼接字符串的办法StringBuilder sql = new StringBuilder("delete from borrow_record where id in (");for (int i = 0; i < ids.length; i++) {if(i != 0) {sql.append(",");}sql.append("?");}sql.append(")");ps = c.prepareStatement(sql.toString());for (int i = 0; i < ids.length; i++) {  //填占位符ps.setInt(i+1,Integer.parseInt(ids[i]));}return ps.executeUpdate();}catch (Exception e) {throw new SystemException("000012","删除图书借阅信息出错",e);}finally {DBUtil.close(c,ps);}}
}

(6)DictionaryTagDAO

import liufc.exception.SystemException;
import liufc.model.DictionaryTag;
import liufc.util.DBUtil;import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;//每个dao都是对应数据库表的jdbc操作
//这里就是对数据字典标签的数据访问操作
public class DictionaryTagDAO {public static List<DictionaryTag> query(String key) {List<DictionaryTag> tags = new ArrayList<>();Connection c = null;PreparedStatement ps = null;ResultSet rs = null;try {c = DBUtil.getConnection();String sql = "select concat(d.dictionary_key, dt.dictionary_tag_key) dictionary_tag_key," +"       dt.dictionary_tag_value" +" from dictionary d" +"         join dictionary_tag dt on d.id = dt.dictionary_id" +" where d.dictionary_key = ?";ps = c.prepareStatement(sql);ps.setString(1,key);rs = ps.executeQuery();while (rs.next()) {DictionaryTag tag = new DictionaryTag();tag.setDictionaryTagKey(rs.getString("dictionary_tag_key"));tag.setDictionaryTagValue(rs.getString("dictionary_tag_value"));tags.add(tag);}}catch (Exception e) {throw new SystemException("00002","查询数据字典信息出错",e);}finally {DBUtil.close(c,ps,rs);}return tags;}
}

5、设计API接口

1、AbstractBaseServlet设置一个统一的HTTP响应类
这是一个继承自HttpServlet的抽象类,设置了一个统一的HTTP响应格式。后面所有的Servlet类都继承自它,增加代码的复用性。

import liufc.exception.BaseException;
import liufc.model.ResponseResult;
import liufc.util.CountHolder;
import liufc.util.JSONUtil;import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;public abstract class AbstractBaseServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doPost(req, resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//对请求体进行编码,注意:对url中的请求数据无效req.setCharacterEncoding("UTF-8");//针对响应体设置编码resp.setCharacterEncoding("UTF-8");//设置响应的数据格式:响应头Content-Type告诉浏览器怎么解析resp.setContentType("text/html");//响应结果ResponseResult r = new ResponseResult();try {//父类的service调用doGet/doPost方法,执行到这里,调用子类的process方法//只要process没有抛出异常,就返回成功的数据Object data = process(req,resp);r.setSuccess(true);r.setCode("200");r.setMessage("操作成功");r.setTotal(CountHolder.get());  //可能是分页的接口,get可以获取到值;也可能不是分页接口,返回nullr.setData(data);} catch (Exception e) {   //process抛异常的处理逻辑e.printStackTrace();if(e instanceof BaseException) {BaseException be = (BaseException) e;r.setCode(be.getCode());r.setMessage(be.getMessage());}else {r.setCode("500");r.setMessage("未知的错误");}//设置堆栈信息StringWriter sw = new StringWriter();PrintWriter pw = new PrintWriter(sw);e.printStackTrace(pw);r.setStackTrace(sw.toString());}finally {CountHolder.remove();   //ThreadLocal规范做法,线程结束前,一定要remove删除,否则就可能出现内存泄漏}PrintWriter pw = resp.getWriter();pw.println(JSONUtil.write(r));pw.flush();}//抽象方法:子类重写public abstract Object process (HttpServletRequest req, HttpServletResponse resp) throws Exception;
}

(2)UserLoginServlet类来校验用户名密码是否正确

import liufc.dao.UserDAO;
import liufc.exception.BusinessException;
import liufc.model.User;
import liufc.util.JSONUtil;import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;@WebServlet("/user/login")
public class UserLoginServlet extends AbstractBaseServlet {@Overridepublic Object process(HttpServletRequest req, HttpServletResponse resp) throws Exception {//req.getParameter("")这个方法只能获取url和请求体,k=v形式的数据User user = JSONUtil.read(req.getInputStream(),User.class);   //http请求解析的用户数据User queryUser = UserDAO.query(user);  //通过请求的用户密码在数据库查询,获取用户if(queryUser == null) {throw new BusinessException("00001","用户名密码校验失败");}HttpSession session = req.getSession();session.setAttribute("user",queryUser);return null;}
}

(3)StudentQueryAsDictServlet类通过学生id查询学生信息

import liufc.dao.StudentDAO;
import liufc.model.Student;import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;@WebServlet("/student/queryAsDict")
public class StudentQueryAsDictServlet extends AbstractBaseServlet{@Overridepublic Object process(HttpServletRequest req, HttpServletResponse resp) throws Exception {//获取班级idint id = Integer.parseInt(req.getParameter("dictionaryKey"));List<Student> students = StudentDAO.queryAsDict(id);return students;}
}

(4)ClassesQueryAsDicServlet类查询班级表

import liufc.dao.ClassesDAO;
import liufc.model.Classes;import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;@WebServlet("/classes/queryAsDict")
public class ClassesQueryAsDicServlet extends AbstractBaseServlet{@Overridepublic Object process(HttpServletRequest req, HttpServletResponse resp) throws Exception {List<Classes> classesList = ClassesDAO.queryAsDict();return classesList;}
}

(5)BookQueryAsDictServlet类查询图书列表

import liufc.dao.BookDAO;
import liufc.model.Book;import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;@WebServlet("/book/queryAsDict")
public class BookQueryAsDictServlet extends AbstractBaseServlet{@Overridepublic Object process(HttpServletRequest req, HttpServletResponse resp) throws Exception {List<Book> books = BookDAO.queryAsDict();return books;}
}

(6)BorrowRecordAddServlet类添加图书借阅信息

import liufc.dao.BorrowRecordDAO;
import liufc.exception.BusinessException;
import liufc.model.BorrowRecord;
import liufc.util.JSONUtil;import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;@WebServlet("/borrowRecord/add")
public class BorrowRecordAddServlet extends AbstractBaseServlet{@Overridepublic Object process(HttpServletRequest req, HttpServletResponse resp) throws Exception {BorrowRecord record = JSONUtil.read(req.getInputStream(),BorrowRecord.class);int num = BorrowRecordDAO.insert(record);  //数据库的插入操作,返回值是intif(num != 1) {throw new BusinessException("00008","插入图书借阅信息数量异常");}return null;}
}

(7)BorrowRecordQueryServlet类查询图书借阅信息

import liufc.dao.BorrowRecordDAO;
import liufc.model.BorrowRecord;
import liufc.model.Page;
import liufc.util.Util;import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;@WebServlet("/borrowRecord/query")
public class BorrowRecordQueryServlet extends AbstractBaseServlet{@Overridepublic Object process(HttpServletRequest req, HttpServletResponse resp) throws Exception {//url中的请求数据通过getParameter获取Page p = Util.parse(req);List<BorrowRecord> records = BorrowRecordDAO.query(p);return records;}
}

(8)BorrowRecordQueryByIdServlet类通过id查询图书借阅信息

import liufc.dao.BorrowRecordDAO;
import liufc.model.BorrowRecord;import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;@WebServlet("/borrowRecord/queryById")
public class BorrowRecordQueryByIdServlet extends AbstractBaseServlet{@Overridepublic Object process(HttpServletRequest req, HttpServletResponse resp) throws Exception {//获取图书借阅记录的idint id = Integer.parseInt(req.getParameter("id"));BorrowRecord record = BorrowRecordDAO.queryById(id);return record;}
}

(9)BorrowRecordUpdateServlet类更新图书借阅信息

import liufc.dao.BorrowRecordDAO;
import liufc.exception.BusinessException;
import liufc.model.BorrowRecord;
import liufc.util.JSONUtil;import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;@WebServlet("/borrowRecord/update")
public class BorrowRecordUpdateServlet extends AbstractBaseServlet{@Overridepublic Object process(HttpServletRequest req, HttpServletResponse resp) throws Exception {BorrowRecord record = JSONUtil.read(req.getInputStream(),BorrowRecord.class);int num = BorrowRecordDAO.update(record);  //数据库的插入操作,返回值是intif(num != 1) {throw new BusinessException("00009","修改图书借阅信息数量异常");}return null;}
}

(10)BorrowRecordDeleteServlet类删除图书借阅信息

import liufc.dao.BorrowRecordDAO;
import liufc.exception.BusinessException;
import liufc.model.BorrowRecord;
import liufc.util.JSONUtil;import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;@WebServlet("/borrowRecord/delete")
public class BorrowRecordDeleteServlet extends AbstractBaseServlet{@Overridepublic Object process(HttpServletRequest req, HttpServletResponse resp) throws Exception {String[] ids = req.getParameterValues("ids");//ids=4,ids=5...int num = BorrowRecordDAO.delete(ids);  //数据库的插入操作,返回值是intif(num != ids.length) {throw new BusinessException("00009","删除图书借阅信息数量异常");}return null;}
}

项目代码结构

Java版图书管理系统完整开发流程相关推荐

  1. java word - 2:POI开发流程

    接上文,本章介绍下POI的开发流程 不同于Freemarker那样将模板和数据分离的方式,POI是没有模板的,模板和数据是糅合在一起的. POI的开发流程其实网上教程都比较多了,我这边就将我写好的工具 ...

  2. 浙里办完整开发流程(仅前端)

    开发之前一定要作的几点 1.添加浙里办钉钉对接群 群号 34143965(有什么不定的地方就大胆问群里的几个负责人) 2.问钉钉群里面得的老师要开发流程相关文件 和开发注意事项(浙里办适老化 对ui有 ...

  3. 项目管理:硬件类项目完整开发流程

    担任过2个硬件类项目的项目经理(同时作为项目开发成员),以下以近期负责的一个项目为参考, 项目成员: 项目经理1名:负责项目各个阶段的监管,同时兼任应用软件工程师 PM 1名:协助项目经理监管项目各个 ...

  4. vue实现饿了么外卖系统(完整开发流程)

    目标 1.掌握vue.js项目在实战中的用 2.学会使用vue.js 3.组件化.模块化的开发方式 涉及知识内容 1.vue-cli 2.axiox ajax通信.vue-resource(更新后不支 ...

  5. JAVA支付宝扫码支付开发流程

    近期工作遇到支付宝扫码支付,经开发研究与3年前相比  API调用与配置更加简单方便,整体开发流程如下(后期会有对应的支付宝APP支付) 登录支付宝开发平台 -->开发接入    链接:https ...

  6. 基于SpringBoot 2.x开发的简易版图书管理系统(实现对图书的CRUD)

    文章目录: 1.项目源码链接 2.项目开发前景 3.项目大体介绍 4.项目整体架构 5.项目运行结果图 6.结束语 1.项目源码链接 GitHub链接:https://github.com/26563 ...

  7. Java简易版图书管理系统(面向小白的万字详解)

  8. SSM项目实例——简易版图书管理系统

    文章目录 概述 准备 实现 1.搭建数据库 2.实现pojo层 3.实现dao层 4.实现service层 5.配置Spring整合dao层 6.配置Spring整合service层 7.配置Spri ...

  9. java融云即时通开发流程,基于融云开发的 Android 版即时通讯(IM)应用程序

    SealTalk-Android Android 应用 SealTalk 由 融云 RongCloud 出品. 特别注意 SealTalk 自从 1.3.14 版本起,CallLib 模块引用的音视频 ...

最新文章

  1. 利用FLASH和XML做炫酷图片相册
  2. 新站长更要努力做好SEO相关的优化
  3. 一块钱哪里去了?--java浮点型背后的故事
  4. Swift - 使用set,get确保索引加减在正常的范围内
  5. 洛谷P1279 字串距离 (动态规划)
  6. linux树莓派扩容,树莓派ubuntu 扩容
  7. Transformers实战系列 之 文本生成
  8. 【收藏】李纪为:初入NLP领域的一些小建议
  9. @PropertySource注解获取配置文件值
  10. FIO压测-SSDvsHDD
  11. 惊!微信可以转发语音了?!
  12. python制作一个超强的加密软件
  13. 利用计算机指令清理垃圾,怎么用命令来清理系统垃圾
  14. 关于mysql的表情包_mysql表情包 - mysql微信表情包 - mysqlQQ表情包 - 发表情 fabiaoqing.com...
  15. GreenDotNet0 1 1发布 Net精简环境 及示例 Net在线漫画下载器
  16. androd欢迎页播放视频和广告页面
  17. 【安信可首款4G模组CA-01直连阿里物联网平台①】产品创建
  18. 如何给SCI收录的文章分区?
  19. CISCO CPOS STM配置示例
  20. 8大软件供应链攻击事件概述

热门文章

  1. ue4手机ui_UE4入门之路(UI篇):UI性能优化
  2. linux中清理缓存命令,linux清理缓存的命令
  3. 经纬度转高斯投影坐标(xy平面坐标)
  4. Shtudown命令的使用
  5. 数据库实验一 数据库定义
  6. Spark Standalone 全分布模式安装及问题配置过程中常见问题
  7. matlab PLSR拟合
  8. redis数据库启动
  9. Selenium firefox浏览器修改为标签页打开方式的各种坑
  10. Chrome插件消息传递实例