SSM框架实战:简单图书管理与借阅系统

前言

在学习完了SSM框架整合开发后,我就想找个项目练练手。虽然说书上有个新闻发布系统可以跟着做,但是篇幅太大了,而且代码不全,还不好改动。于是我就在网上到处找推荐的项目,就找到了图书馆这种类型的。在结合了到处看看和书本上所学的开发套路后,我自己写了个简单的图书管理与借阅系统,虽说不是很高大上,但也算是个“麻雀”了吧。
写这个项目的目的是将书本上所学的SSM知识融会贯通并且实际应用起来,过程虽然有些艰辛,但也时受益颇多。而写博客的目的一方面也是锻炼一下自己对整个框架的总结分析能力,另一方面是给后面跟我有同样想法的朋友们一个教程去学习。第一次写,写的不好的地方希望大家指出,也希望有建议的朋友留言,谢谢!

一 系统总体框架

图示

总体架构

各部分解释

画的不好看,各位看官莫怪。其中带箭头的线条表示点击某个链接后会跳转到箭头所指向的那个页面。管理员和用户旁边的数字表示权限,线条上的文字表示可以进行的操作

1.主页面,第一次进去时只有查询图书按钮,以及请登录的标志

2.查询图书页面,里面展示了图书馆的所有书籍,包括序号、书名、数量、描述、操作按钮(有修改、删除、借书)

3.管理员进行书籍的添加和修改页面,其实它们两个页面是不一样的,但是显示出来的效果总体上差不多,所有我就不多画了

4.用户查看他借阅的书籍界面,某个用户所借的所有书都会显示在这里,包括序号、书名、描述、还书操作。没有数量一列的原因是我规定了每人只能借一本同样的书

5.用户登录界面,提供注册按钮

6.用户注册界面

7.消息显示界面,在后端有很多接口会将消息存入model中,然后跳转到information.jsp界面进行显示(虽然这挺蠢的,但是我觉得这样做简单)

8.我本来想实现这3个功能,毕竟这是SSM学习的重要内容,特别是AOP。然而我比较懒,只实现了前2个,AOP日志记录没有实现,大家可以自己尝试

二 数据库设计

该项目需要的数据库表包括user、books、borrow。borrow是一个中间表(用户与书之间是多对多关系),记录用户借书

2.1 创建数据库

create database db_ssm;

2.2 创建user表

user表包含了userid、用户名、账号、密码、权限。注意登录时是需要账号和密码,用户名只是个昵称

CREATE TABLE `user` (
  `userid` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(10) DEFAULT 'library',
  `account` varchar(10) NOT NULL,
  `password` varchar(20) NOT NULL,
  `permission` int(11) NOT NULL,
  PRIMARY KEY (`userid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 

由于管理员不能通过注册来创建,所以直接在服务器端数据库插入一个管理员账号,方便之后管理书籍

insert into user(username,account,password,permission) values('admin','root','123456',1);

在这里插入图片描述

2.3 创建books表

books表包含了bookid、书名、数量、描述

CREATE TABLE `books` (
  `bookid` int(11) NOT NULL AUTO_INCREMENT,
  `bookname` varchar(20) NOT NULL,
  `bookcount` int(11) NOT NULL,
  `detail` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`bookid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 

2.4 创建borrow表

borrow表包含了主键id、两个外键userid和bookid,分别关联到前面的两个表

CREATE TABLE `borrow` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `bookid` int(11) DEFAULT NULL,
  `userid` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `bookid` (`bookid`),
  KEY `userid` (`userid`),
  CONSTRAINT `borrow_ibfk_1` FOREIGN KEY (`bookid`) REFERENCES `books` (`bookid`),
  CONSTRAINT `borrow_ibfk_2` FOREIGN KEY (`userid`) REFERENCES `user` (`userid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

三 准备工作

3.1 运行环境

①开发平台:IDEA2020.1
②数据库版本:MySQL5
③JDK版本:JDK15(用JDK8一样可以的)
④Tomcat版本:Tomcat9
⑤插件:Lombok、MybatisX(非必要,简化开发)

3.2 项目创建和配置

①创建一个名为easy_library的javaweb项目
②在web > WEB-INF文件夹下创建两个目录lib、classes
③打开项目结构设置Project Structrue,进行如下设置
项目结构1
项目结构2
项目结构3
④按照下面的目录结构创建相应的包和目录

3.3 目录结构

在这里插入图片描述

3.4 Tomcat配置

Tomcat的配置很简单,按照下面的图片操作即可
tomcat1
tomcat2
tomcat3
tomcat4

3.5 所需jar包和图片

下图是项目所需的jar包,有些可能不是必须的,我是把SSM整合需要的全部都放在一起了,每次需要直接复制就行(文末有整个项目源码和lib包资源)
lib1
lib2
将这些jar包复制到项目的lib文件夹下后,在IDEA中右键点击lib,选择add as library,如图所示:
lib3

------------------------------------------------分割线-------------------------------------------
准备页面需要的几张背景图片,如下所示,分别命名为library.jpg、listBooks.jpg、bookInfo.jpg、userBooks.jpg、desert.png,并放到web > images文件夹下。这些图片是从百度上随便找来的,在此感谢提供图片的人。大家也可以选别的图片,只要会在后面的jsp文件中对应的位置改路径就行
library
listBooks
bookInfo
userBooks
desert
------------------------------------------------分割线-------------------------------------------
将编写ajax代码所需的文件jquery.min.js复制到web > js文件夹下,如图所示:
js

四 SSM框架搭建

框架搭建就是让三个框架能够协同工作,发挥各自的优势,让开发工作变得更加简单,主要内容就是编写一些配置文件的信息

4.1 log4j.properties

mybatis使用log4j.properties文件来输出它执行的SQL语句,可以在IDEA控制台看到这些信息:
log4j
在src目录下新建log4j.properties文件并输入以下内容:

log4j.rootLogger=ERROR, stdout
log4j.logger.com.ssm=DEBUG
Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

4.2 db.properties

在resources包下创建db.properties文件,输入数据库基本信息(我用的mysql驱动包是mysql8,所以标绿的地方与mysql5不一样,其他版本可以百度)

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/db_ssm?serverTimezone=UTC
jdbc.username=数据库账号
jdbc.password=数据库密码
jdbc.maxTotal=10        
jdbc.maxIdle=10         
jdbc.initialSize=5      

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/db_ssm?serverTimezone=UTC

4.3 mybatis-config.xml

在resources包下创建mybatis-config.xml,输入以下内容:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!-- 返回自动生成的主键值 -->
    <settings>
        <setting name="useGeneratedKeys" value="true"/>
    </settings>
    <!-- 别名 -->
    <typeAliases>
        <package name="com.ssm.pojo"/>
    </typeAliases>
</configuration>

4.4 springmvc-config.xml

在resources包下创建springmvc-config.xml文件,输入以下内容。由于我是在controller层进行事务管理,所以就将事务配置在了这个文件中

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

    <!-- 扫描controller注解 -->
    <context:component-scan base-package="com.ssm.controller"/>

    <!-- 配置事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- 开启注解驱动 -->
    <mvc:annotation-driven/>
    <tx:annotation-driven transaction-manager="transactionManager"/>

    <!-- 配置静态资源的访问权限,不会被前端控制器拦截 -->
    <mvc:resources mapping="/js/**" location="/js/"/>
    <mvc:resources mapping="/images/**" location="/images/"/>
    <mvc:resources mapping="/css/**" location="/css/"/>

    <!--视图解析器,简化视图的编写路径-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

</beans>

4.5 applicationContext.xml

在resources包下创建applicationContext.xml,输入以下内容:

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

    <!-- 读取db.properties -->
    <context:property-placeholder location="classpath:resources/db.properties"/>
    <!-- 配置数据源 -->
    <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
        <property name="maxTotal" value="${jdbc.maxTotal}"/>
        <property name="maxIdle" value="${jdbc.maxIdle}"/>
        <property name="initialSize" value="${jdbc.initialSize}"/>
    </bean>

    <!-- 配置mybatis工厂,单例模式 -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean" scope="singleton">
        <!-- 注入数据源 -->
        <property name="dataSource" ref="dataSource"/>
        <!-- 指定mybatis-config配置文件的位置 -->
        <property name="configLocation" value="classpath:resources/mybatis-config.xml"/>
    </bean>

    <!-- 配置SqlSessionTemplate -->
    <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
        <constructor-arg ref="sqlSessionFactory"/>
    </bean>

    <!-- 配置mapper扫描器 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.ssm.dao"/>
        <property name="sqlSessionTemplateBeanName" value="sqlSessionTemplate" />
    </bean>

    <!-- 扫描Service层 -->
    <context:component-scan base-package="com.ssm.service"/>
</beans>

4.6 web.xml

编辑web.xml文件信息,加入spring文件监听器、编码器、前端控制器:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
         http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         id="WebApp_ID" version="3.0">
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>

    <!-- 配置加载Spring文件的监听器 -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:resources/applicationContext.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- 编码过滤器 -->
    <filter>
        <filter-name>encoding</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>
    </filter>
    <filter-mapping>
        <filter-name>encoding</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!--配置前端过滤器-->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--初始化时加载过滤器-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:resources/springmvc-config.xml</param-value>
        </init-param>
        <!--自启动-->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

五 后端功能实现

5.1 持久层pojo

此层的类都是与数据库一一对应的,形成ORM关系,就是存数据的地方。所以,类中的字段要与数据库列名保持一致。下面出现的@Data注解是Lombok提供的,可以自动生成setter、getter、toString等方法,但是需要下载对应插件和引入依赖。大家也可以不用,直接在类中生成即可

5.1.1 书类Bookxx

@Data
public class Bookxx {
    private Integer bookid;
    private String bookname;
    private String detail;
    private Integer bookcount;
}

为什么多了xx? 因为有一个不知道什么包的类重名了,所以最好不要冲突
Book类

5.1.2 用户类User

@Data
public class User {
    private Integer userid;
    private String username;
    private String account;
    private String password;
    private Integer permission;
    private List<Bookxx> books;
}

5.2 数据访问层dao

此层就是写一些Interface和.xml文件来进行数据库的操作,即CRUD

5.2.1 BookDao

创建BookDao接口和对应的BookDao.xml文件,编写一些与操作books表相关的方法和SQL语句

public interface BookDao {

    /**
     * 返回所有书目信息
     * @return 所有的书目信息
     */
    List<Bookxx> listBooks();

    /**
     * 根据书名查找一本书的信息
     * @param bookname 书目id
     * @return 一本书的信息
     */
    Bookxx findBookByName(@Param("bookname") String bookname);

    /**
     * 插入新书
     * @param book 要插入的书信息
     * @return
     */
    int addBook(Bookxx book);

    /**
     * 根据id删除书的信息
     * @param bookid 要删除的书目id
     * @return
     */
    int delBook(@Param("bookid") Integer bookid);

    /**
     * 更新一本书的信息
     * @param book 要更新后的新书信息
     * @return
     */
    int updateBook(Bookxx book);

    /**
     * 用户借书
     * @param bookid 书名
     * @return
     */
    int borrowBook(Integer bookid);

    /**
     * 用户还书
     * @param bookid
     * @return
     */
    int returnBook(Integer bookid);

}
<?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">

<mapper namespace="com.ssm.dao.BookDao">
    <insert id="addBook" keyProperty="bookid">
        insert into
        <choose>
            <when test="detail!=null and detail!=''">
                books (bookname, bookcount, detail) value (#{bookname}, #{bookcount}, #{detail})
            </when>
            <otherwise>
                books (bookname, bookcount) value (#{bookname}, #{bookcount})
            </otherwise>
        </choose>
    </insert>
    <update id="updateBook" flushCache="true">
        update books
        <set>
            <if test="bookname!=null and bookname!=''">
                bookname = #{bookname},
            </if>
            <if test="bookcount!=null and bookcount!=''">
                bookcount = #{bookcount},
            </if>
            <if test="detail!=null and detail!=''">
                detail = #{detail},
            </if>
        </set>
        where bookid = #{bookid}
    </update>
    <update id="borrowBook">
        update books
        set bookcount = bookcount - 1
        where bookid = #{bookid}
    </update>
    <update id="returnBook">
        update books
        set bookcount = bookcount + 1
        where bookid = #{bookid}
    </update>
    <delete id="delBook" flushCache="true">
        delete
        from books
        where bookid = #{bookid}
    </delete>
    <select id="listBooks" resultType="com.ssm.pojo.Bookxx">
        select *
        from books
    </select>

    <select id="findBookByName" resultType="com.ssm.pojo.Bookxx">
        select *
        from books
        where bookname = #{bookname}
    </select>
</mapper>

5.2.2 UserDao

创建UserDao接口和对应的UserDao.xml文件,编写一些与操作user表相关的方法和SQL语句。其中UserDao.xml文件中定义了一个resultMap,处理用户和书之间多对多的关联映射,用了嵌套结果的方法

public interface UserDao {

    /**
     * 通过账号查找用户是否存在,用于用户注册
     * @param account 账号
     * @return 用户存在或null
     */
    User findUserByAccount(@Param("account") String account);

    /**
     * 用于用户登录
     * @param account 账号
     * @param password 密码
     * @return
     */
    User getUser(@Param("account") String account, @Param("password") String password);

    /**
     * 用户注册
     * @param user 用户注册的信息
     * @return
     */
    int addUser(@Param("user") User user);

    /**
     * 返回一个用户及其所借书籍的信息
     * @param userid 用户id
     * @return
     */
    User listBooksByUserid(@Param("userid") Integer userid);

}
<?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">

<mapper namespace="com.ssm.dao.UserDao">
    <!-- 多对多结果映射 -->
    <resultMap type="user" id="userMap">
        <id column="userid" property="userid"/>
        <result column="username" property="username"/>
        <result column="account" property="account"/>
        <result column="password" property="password" />
        <result column="permission" property="permission" />
        <collection property="books" ofType="bookxx">
            <id column="bookid" property="bookid"/>
            <result column="bookname" property="bookname"/>
            <result column="detail" property="detail"/>
        </collection>
    </resultMap>


    <insert id="addUser" keyProperty="userid">
        insert into
        user (username, account, password, permission)
        value (#{user.username}, #{user.account}, #{user.password}, #{user.permission})
    </insert>
    <select id="findUserByAccount" resultType="com.ssm.pojo.User">
        select *
        from user
        where account = #{account}
    </select>
    <select id="findUserByUid" resultType="com.ssm.pojo.User">
        select *
        from user
        where userid = #{userid}
    </select>
    <select id="getUser" resultType="com.ssm.pojo.User">
        select *
        from user
        where account = #{account} and password = #{password}
    </select>
    <select id="listBooksByUserid" resultMap="userMap">
        select user.*, books.*
        from user, books, borrow
        where user.userid = #{userid}
        and borrow.userid = user.userid
        and borrow.bookid = books.bookid
    </select>
</mapper>

5.2.3 UserAndBookDao

创建UserAndBookDao接口和对应的UserAndBookDao.xml文件,编写一些与操作borrow表相关的方法和SQL语句,记录用户借书与还书

public interface UserAndBookDao {

    /**
     * 用户借书
     * @param bookid
     * @param userid
     * @return
     */
    int borrowBook(@Param("bookid") Integer bookid, @Param("userid") Integer userid);

    /**
     * 用户还书
     * @param bookid
     * @param userid
     * @return
     */
    int retuenBook(@Param("bookid") Integer bookid, @Param("userid") Integer userid);

    /**
     * 判断用户是否已经借过此书(假设每种书每人只能借一本)
     * @param bookid
     * @param userid
     * @return
     */
    Integer checkIfBorrow(@Param("bookid") Integer bookid, @Param("userid") Integer userid);
}
<?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">

<mapper namespace="com.ssm.dao.UserAndBookDao">

    <insert id="borrowBook">
        insert into
        borrow (bookid, userid)
        values (#{bookid}, #{userid})
    </insert>
    <delete id="retuenBook">
        delete from
        borrow
        where bookid = #{bookid} and userid = #{userid}
    </delete>
    <select id="checkIfBorrow" resultType="java.lang.Integer">
        select id
        from borrow
        where kooid = #{bookid} and userid = #{userid}
    </select>
</mapper>

5.3 业务层service

此层我现在还不知道该写哪些东西,人人都说写业务逻辑,但是我对业务也没有很深的认识,可能是我没上过实战的原因吧。所以我就按照书本教我的来写了,也就是每个业务接口实现类都是返回数据库执行后的结果,没有其他多余的操作。
接口代码略,直接上实现类的

5.3.1 BookService

创建BookService接口及其实现类BookServiceImpl,注入BookDao依赖

@Service
public class BookServiceImpl implements BookService {
    /**
     * 依赖注入
     */
    @Autowired
    private BookDao bookDao;

    @Override
    public List<Bookxx> listBooks() {
        return bookDao.listBooks();
    }

    @Override
    public Bookxx findBookByName(String bookname) {
        return bookDao.findBookByName(bookname);
    }

    @Override
    public int updateBook(Bookxx book) {
        return bookDao.updateBook(book);
    }

    @Override
    public int delBool(Integer bookid) {
        return bookDao.delBook(bookid);
    }

    @Override
    public int addBook(Bookxx book) {
        return bookDao.addBook(book);
    }

    @Override
    public int borrowBook(Integer bookid) {
        return bookDao.borrowBook(bookid);
    }

    @Override
    public int returnBook(Integer bookid) {
        return bookDao.returnBook(bookid);
    }
}

5.3.2 UserService

创建UserService接口及其实现类UserServiceImpl,注入UserDao依赖

@Service
public class UserServiceImpl implements UserService {

    /**
     * 依赖注入
     */
    @Autowired
    private UserDao userDao;

    @Override
    public User findUserByAccount(String account) {
        return userDao.findUserByAccount(account);
    }

    @Override
    public User getUser(String account, String password) {
        return userDao.getUser(account, password);
    }

    @Override
    public int addUser(User user) {
        return userDao.addUser(user);
    }

    @Override
    public User listBooksByUserid(Integer userid) {
        return userDao.listBooksByUserid(userid);
    }
}

5.3.3 UserAndBookService

创建UserAndBookService接口及其实现类UserAndBookServiceImpl,注入UserAndBookDao依赖

@Service
public class UserAndBookServiceImpl implements UserAndBookService {

    /**
     * 依赖注入
     */
    @Autowired
    private UserAndBookDao userAndBookDao;

    @Override
    public int borrowBook(Integer bookid, Integer userid) {
        return userAndBookDao.borrowBook(bookid, userid);
    }

    @Override
    public int retuenBook(Integer bookid, Integer userid) {
        return userAndBookDao.retuenBook(bookid, userid);
    }

    @Override
    public Integer checkIfBorrow(Integer bookid, Integer userid) {
        return userAndBookDao.checkIfBorrow(bookid, userid);
    }
}

5.4 web接口层controller

此层就是写一些前后端交互的接口,有时候可能也会把业务逻辑写到了这里来

5.4.1 UserController

创建UserController类,注入UserService依赖,编写一些与用户相关的接口。由于这是个简单的练手项目,也不需要太复杂的用户操作,所以就暂时先实现用户登录、注册、退出、展示用户所借书籍就行了,其他的如管理用户、修改密码就不需要了。由于jsp资源在浏览器是访问不到的,所以需要写一个toXXX接口给用户跳转

@Controller
@RequestMapping("/user")
public class UserController {

    /**
     * 依赖注入
     */
    @Autowired
    private UserService userService;

    /**
     * 跳转至登录界面
     * @return
     */
    @RequestMapping("/toLogin")
    public String toLogin(){
        return "login";
    }

    /**
     * 用户登录验证
     * @param account 账号
     * @param password 密码
     * @param session 存登录信息
     * @param model
     * @return
     */
    @GetMapping("/login")
    public String login(String account, String password, HttpSession session, Model model){
        User user = userService.getUser(account, password);
        if (user!=null){
            // 在会话中保存登录信息
            session.setAttribute("login", user);
            model.addAttribute("msg", "登录成功");
            return "information";
        }
        return "login";
    }

    /**
     * 用户退出登录
     * @param session 登录会话
     * @return
     */
    @RequestMapping("/logout")
    public String logout(HttpSession session){
        session.invalidate();
        return "redirect:/index.jsp";
    }

    /**
     * 跳转至注册界面
     * @return
     */
    @RequestMapping("/toAddUser")
    public String toAddUser(){
        return "addUser";
    }

    /**
     * 用户注册
     * @param user 用户注册时填的信息
     * @param model
     * @return
     */
    @PostMapping("/addUser")
    public String addUser(User user, Model model){
        // 查看是否存在同账号的另一个用户
        User check = userService.findUserByAccount(user.getAccount());
        if (check == null){
            // 判断插入语句执行是否成功
            int affected = userService.addUser(user);
            if (affected > 0){
                model.addAttribute("msg", "注册成功, 你的用户id是 " + user.getUserid());
            } else {
                model.addAttribute("msg", "注册失败, 请重试");
            }
        } else {
            model.addAttribute("msg", "注册失败, 账号已存在");
        }
        return "information";
    }

    /**
     * 查看用户所借书籍信息
     * @param userid
     * @param model
     * @return
     */
    @RequestMapping("/listUserBooks")
    public String listUserBooks(Integer userid, Model model){
        /*
        *  数据库查询会返回很多条同一个用户的记录,只是书本的信息不同
        *  如果用户没有借书,则还会返回null,所以要进行判断
        *  因为最终要呈现到浏览器上的是书本信息,所以只提取books数据即可
        */
        User user = userService.listBooksByUserid(userid);
        if (user != null) {
            model.addAttribute("books", user.getBooks());
        }
        return "userBooks";
    }

}

5.4.2 BookController

创建BookController类,注入BookService和UserAndBookService依赖,编写一些与书本操作相关的接口。根据总体框架来看,需要的接口包括:展示所有书籍信息,管理员删除、添加、更新书籍信息,用户借书、还书。同时,由于一些接口设计事务管理,所以需要加上@Transactional注解。要想验证事务是否生效,可以在语句中显式加入异常,例如借书和还书

@Controller
@RequestMapping("/book")
public class BookController {
    /**
     * 依赖注入
     */
    @Autowired
    private BookService bookService;
    @Autowired
    private UserAndBookService userAndBookService;

    /**
     * 显示所有书目信息的接口
     * @param model
     * @return
     */
    @RequestMapping("/listBooks")
    public String listBooks(Model model){
        List<Bookxx> books = bookService.listBooks();
        model.addAttribute("books", books);
        return "listBooks";
    }

    /**
     * 根据id删除特定书目
     * @param bookid
     * @param model
     * @return
     */
    @GetMapping("/delBook")
    @Transactional
    public String delBook(@RequestParam("bookid") Integer bookid, Model model){
        int affected = bookService.delBool(bookid);
        if (affected > 0){
            model.addAttribute("msg", "成功删除id为 " + bookid + " 的书:");
        } else {
            model.addAttribute("msg", "删除失败");
        }
        return "information";
    }

    /**
     * 转到添加书目信息的界面
     * @return
     */
    @RequestMapping("/toAddBook")
    public String toAddBook(){
        return "addBook";
    }

    /**
     * 添加新书
     * @param book 新书的信息
     * @param model
     * @return
     */
    @PostMapping("/addBook")
    @Transactional
    public String addBook(Bookxx book, Model model){
        // 查看是否存在同名书籍(不允许同名书籍存在)
        Bookxx check = bookService.findBookByName(book.getBookname());
        if (check != null){
            model.addAttribute("msg", "添加失败, 已存在同名书籍");
        } else {
            int affected = bookService.addBook(book);
            if (affected > 0) {
                model.addAttribute("msg", "添加成功, 新书目的id为 " + book.getBookid());
            } else {
                model.addAttribute("msg", "添加失败");
            }
        }
        return "information";
    }

    /**
     * 转到更新书目信息
     * @param model
     * @param bookid
     * @return
     */
    @RequestMapping("/toUpdateBook/{bookid}")
    public String toUpdateBook(Model model, @PathVariable Integer bookid){
        model.addAttribute("bookid", bookid);
        return "updateBook";
    }

    /**
     * 更新书目信息
     * @param book 需要更新的书的新信息
     * @param model
     * @return
     */
    @PostMapping("/updateBook")
    @Transactional
    public String updateBook(Bookxx book, Model model){
        int affected = bookService.updateBook(book);
        if (affected > 0){
            model.addAttribute("msg", "更新成功");
        } else {
            model.addAttribute("msg", "更新失败");
        }
        return "information";
    }

    /**
     * 用户借书
     * @param request json格式信息,包括bookid和userid
     * @return response json格式信息,包括info
     */
    @RequestMapping("/borrowBook")
    @ResponseBody
    @Transactional
    public Map<String, String> borrowBook(@RequestBody Map<String, String> request){
        int bookid = Integer.parseInt(request.get("bookid"));
        int userid = Integer.parseInt(request.get("userid"));
        Map<String, String> response = new HashMap<>(1);
        // 判断是否存在借过此书的记录(不允许借同一本)
        Integer ifBorrow = userAndBookService.checkIfBorrow(bookid, userid);
        if (ifBorrow != null){
            response.put("info", "借书失败, 你已借过此书");
        } else {
            bookService.borrowBook(bookid);
            //int i = 10/0;
            userAndBookService.borrowBook(bookid, userid);
            response.put("info", "借书成功");
        }
        return response;
    }

    /**
     * 用户还书
     * @param request json格式信息,包括bookid和userid
     * @return response json格式信息,包括info
     */
    @RequestMapping("/returnBook")
    @ResponseBody
    @Transactional
    public Map<String, String> returnBook(@RequestBody Map<String, String> request){
        int bookid = Integer.parseInt(request.get("bookid"));
        int userid = Integer.parseInt(request.get("userid"));
        Map<String, String> response = new HashMap<>(1);
        userAndBookService.retuenBook(bookid, userid);
        //int []i = new int[3];
        //i[3] = 3;
        bookService.returnBook(bookid);
        response.put("info", "还书成功");
        return response;
    }
}

5.5 实现Interceptor

springmvc中的拦截器相当于servlet中的过滤器,一般用于用户登录、权限验证、日志记录、性能检测等场合

5.5.1 登录认证

创建LoginInterceptor类并实现HandlerInterceptor接口,重写preHandle方法,在方法体中取出会话中的"login"属性,查看是否存在登录信息,如果没有就跳转到登录界面

public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        // 对登录信息进行验证
        HttpSession session = httpServletRequest.getSession();
        User user = (User) session.getAttribute("login");
        if (user != null){
            return true;
        }
        httpServletRequest.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(httpServletRequest, httpServletResponse);
        return false;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {

    }
}

5.5.2 权限验证

创建AuthenticateInterceptor类并实现HandlerInterceptor接口,重写preHandle方法,在方法体中进行浏览器请求资源路径的判断,再对用户权限的判断(1为管理员,2为普通用户),允许特定权限访问特定资源

public class AuthenticateInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        String uri = httpServletRequest.getRequestURI();
        User user = (User) httpServletRequest.getSession().getAttribute("login");

        // 对用户或管理员开放不同权限
        if (uri.contains("listUserBooks") || uri.contains("borrowBook") || uri.contains("returnBook")){
            if (user.getPermission() == 2){
                return true;
            }
        } else {
            if (user.getPermission() == 1){
                return true;
            }
        }
        return false;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {

    }
}

5.5.3 配置拦截器

在springmvc-config.xml文件中配置<mvc:interceptors>子元素信息,对某些资源拦截,而对另一些资源放行。注意<mvc:mapping>和<mvc:exclude-mapping>子元素可以配置多个,并且要按照顺序来

在springmvc-config.xml文件中加入以下内容

<mvc:interceptors>
        <!-- 配置登录认证拦截器 -->
        <mvc:interceptor>
            <mvc:mapping path="/user/listUserBooks"/>
            <mvc:mapping path="/book/**"/>
            <mvc:exclude-mapping path="/book/listBooks"/>
            <bean class="com.ssm.interceptor.LoginInterceptor"/>
        </mvc:interceptor>
        <!-- 配置权限验证拦截器 -->
        <mvc:interceptor>
            <mvc:mapping path="/user/listUserBooks"/>
            <mvc:mapping path="/book/**"/>
            <mvc:exclude-mapping path="/book/listBooks"/>
            <bean class="com.ssm.interceptor.AuthenticateInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>

六 前端页面实现

由于我主要熟悉后端程序,对前端不是很懂,所以写页面的过程还是很艰辛的,还让一个懂前端的同学协助我一起完成,最后弄出来的也不是很好看。但是我的目标还是实现前后端正常交互即可,所以只要功能完美实现了,展示的问题就不要那么在意啦。虽然说对前端没有太高要求,但是还是要熟悉一下jsp文件的编写的(这东西是真傻逼,杂糅了java、html、js的内容,很难看)。对于页面编写我就不做过多的解释了,在文件中需要的地方我会用注释写出来的。特别需要提的一点是jsp文件中会使用大量的${pageContext.request.contextPath},它的作用是直接定位到web目录(我也不是很懂,是从书本上跟着学来的,大家可以不用它,但是要注意路径的编写)

6.1 index.jsp

修改index.jsp文件的内容

<%@ page import="com.ssm.pojo.User" %>
<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" language="java" %>
<html>
  <head>
    <meta charset="UTF-8">
    <title>welcome to library</title>
    <style type="text/css">
      *{
        margin: 0;
        padding: 0;
      }
      html,body{
        width: 100%;
        height: 100%;
      }
      p{
        font-family: STHUp;
        clear: both;
        text-align: center;
        font-size: 40px;
        font-weight: bold;
        color: darkorange;
      }
      button{
        margin-top: 150px;
        background-color: transparent;
      }
      .button1{
        float: left;
        margin-left: 33%;
      }
      .button2{
        float: right;
        margin-right: 33%;
      }
      a{
        text-decoration: none;
      }
      .a1{
        color: aquamarine;
      }
      .a2{
        color: darkorchid;
      }
      body{
        background-image: url("${pageContext.request.contextPath}/images/library.jpg");
        background-size: 100% 100%;
        width: 100%;
        height: 100%;
        background-repeat: no-repeat;
      }
    </style>
  </head>
  <body>
    <div>
      <%-- 先取出用户登录信息 --%>
      <%
        User user = (User) session.getAttribute("login");
        request.setAttribute("user", user);
      %>
      <%-- 登录提示 --%>
      <% if (user == null){ %>
        <a href="${pageContext.request.contextPath}/user/toLogin" style="text-decoration:underline;"><p>请登录 </p></a>
      <% } else { %>
      <%-- 欢迎界面 --%>
        <p >你好, ${user.username} </p>
        <button style="float: left; left: calc(50% + 6rem); top: 20px; position:absolute; margin: 0"><a href="${pageContext.request.contextPath}/user/logout"> 退出登录</a> </button>
      <% } %>

      <%-- 任何人都可以查询图书信息 --%>
      <button class="button1">
        <a class="a1" href="${pageContext.request.contextPath}/book/listBooks"><h1>查询图书</h1></a>
      </button>

      <%-- 管理员才可以添加图书 --%>
      <% if(user != null && user.getPermission() == 1) { %>
        <button class="button2">
          <a class="a2" href="${pageContext.request.contextPath}/book/toAddBook"><h1>添加图书</h1></a>
        </button>
      <% } else if (user != null && user.getPermission() == 2) {%>
      <%-- 用户才有我的借阅 --%>
        <button class="button2">
          <a class="a2" href="${pageContext.request.contextPath}/user/listUserBooks?userid=${sessionScope.get("login").userid}"><h1>我的借阅</h1></a>
        </button>
      <% } %>
    </div>
  </body>
</html>

6.2 returnIndex.jsp

在jsp文件夹中创建returnIndex.jsp文件,作用是定义一个“返回到首页”的按钮,可以被许多页面通过<jsp:include page=“returnIndex.jsp” />引用,就不需要每个页面都写很多相同的jsp语句了

<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" language="java" %>
<html>
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="${pageContext.request.contextPath}/index.jsp" >
        <input type="submit" value="返回主页面" >
    </form>
</body>
</html>

6.3 information.jsp

在jsp文件夹中创建information.jsp文件,后端有很多接口会保存一个msg消息到model中,然后跳转至该文件,所以该文件的作用就是显示出那些消息。同时还有一个“确定”按钮,点击后会返回到主页面index.jsp

<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" language="java" %>
<html>
<head>
    <meta charset="UTF-8">
    <title>消息</title>
</head>
<body>
    <div style="text-align: center; font-size: 40px; color: deepskyblue">
        <h1>
            ${msg}
        </h1>
    </div>
    <div style="text-align: center">
        <form style="position: relative; width: 80px; height: 50px; left: 47%" action="${pageContext.request.contextPath}/index.jsp" >
            <input type="submit" value="确定" style="width: 100%; height: 100%">
        </form>
    </div>
</body>
</html>

6.4 listBooks.jsp

在jsp文件夹中创建listBooks.jsp文件,作用是显示图书馆中所有的书、数量、描述、操作,我没有实现分页功能(感觉有点麻烦就懒得弄了)

<%@ page import="com.ssm.pojo.User" %>
<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <meta charset="UTF-8">
    <title>图书列表</title>
    <script type="text/javascript" src="${pageContext.request.contextPath}/js/jquery.min.js"></script>
    <style type="text/css">
        html,body{
            width: 100%;
            height: 100%;
        }
        body{
            background-image: url("${pageContext.request.contextPath}/images/listBooks.jpg");
            background-size: 100% 100%;
            width: 100%;
            height: 100%;
            background-repeat: no-repeat;
        }
        table,th,td{
            border: 1px solid red ;
            border-collapse: collapse;
        }
    </style>
</head>
<body>
    <%-- 先取出用户登录信息 --%>
    <%
        User user = (User) session.getAttribute("login");
        request.setAttribute("user", user);
    %>
    <div
    class="container" style="width: 630px; position:relative; left: 30%; top: 5%; background-color: white">
    <table class="tablelist">
        <thead>
        <tr>
            <th style="width: 50px; text-align: center">序号</th>
            <th style="width: 140px; text-align: center">书名</th>
            <th style="width: 50px; text-align: center">数量</th>
            <th style="width: 310px; text-align: center">描述</th>
            <th style="width: 100px;text-align: center">操作</th>
        </tr>
        </thead>
        <tbody>
        <%-- 先确定有书可展示 --%>
        <c:if test="${books != null}" >
            <c:forEach items="${books}" var="book" varStatus="st">
                <tr style="height: 35px">
                    <td align="center">${st.count}</td>
                    <td align="center">${book.bookname}</td>
                    <td align="center">${book.bookcount}</td>
                    <td align="center">${book.detail}</td>
                    <td align="center">
                        <%-- 对不登录、普通用户、管理员提供不同的操作 --%>
                        <% if (user == null) { %>
                            无操作
                        <% } else if (user.getPermission() == 2) { %>
                            <button onclick="borrowBook(${book.bookcount}, ${book.bookid}, ${user.userid})"><a class="btn btn-primary btn-xs">借书</a></button>
                        <% } else { %>
                            <button style="float: left"><a href="${pageContext.request.contextPath}/book/toUpdateBook/${book.bookid}" class="btn btn-primary btn-xs">修改</a></button>
                            <button style="float: right"><a href="${pageContext.request.contextPath}/book/delBook?bookid=${book.bookid}" class="btn btn-primary btn-xs">删除</a></button>
                        <% } %>
                    </td>
                </tr>
            </c:forEach>
        </c:if>
        </tbody>
    </table>
    <jsp:include page="returnIndex.jsp" />
    </div>

    <script type="text/javascript">
        // 借书用ajax传递json格式信息
        function borrowBook(bookcount, bookid, userid) {
            if (bookcount == 0){
                alert("无书可借");
                return;
            }
            $.ajax({
                url:"${pageContext.request.contextPath}/book/borrowBook",
                type:"post",
                data:JSON.stringify({bookid:bookid, userid:userid}),
                contentType:"application/json;charset=UTF-8",
                success: function (data) {
                    // 显示后端返回的结果
                    alert(data.info);
                    // 刷新本页面
                    location.reload();
                }
            })
        }
    </script>
</body>

6.5 addUser.jsp

在jsp文件夹中创建addUser.jsp文件,作用是提供用户注册界面,这个文件的编写是参考这个博客的注册界面的,同时里面的背景图片也是用他的,包括在登录界面上的背景图片。注册界面包括用户名(<10)、账号(<10)、(确认)密码(6-20)、一个被隐藏的输入框用于默认提交权限为2的值,用一个js函数判断输入的合法性

<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" language="java" %>
<html>
<head>
    <meta charset="UTF-8">
    <title>新用户注册</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        html {
            height: 100%;
            width: 100%;
            overflow: hidden;
            margin: 0;
            padding: 0;
            background: url("${pageContext.request.contextPath}/images/desert.png") no-repeat 0px 0px;
            background-repeat: no-repeat;
            background-size: 100% 100%;
            -moz-background-size: 100% 100%;
        }

        body {
            display: flex;
            align-items: center;
            justify-content: center;
            height: 100%;
        }

        #loginDiv {
            width: 37%;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 650px;
            background-color: rgba(75, 81, 95, 0.3);
            box-shadow: 7px 7px 17px rgba(52, 56, 66, 0.5);
            border-radius: 5px;
        }

        #name_trip {
            margin-left: 50px;
            color: red;
        }

        .sexDiv>input,
        .hobby>input {
            width: 30px;
            height: 17px;
        }

        input,
        select {
            margin-left: 15px;
            border-radius: 5px;
            border-style: hidden;
            height: 30px;
            width: 140px;
            background-color: rgba(216, 191, 216, 0.5);
            outline: none;
            color: #f0edf3;
            padding-left: 10px;
        }
        p {
            margin-top: 10px;
            margin-left: 20px;
            color: azure;
        }
        .button {
            border-color: cornsilk;
            background-color: rgba(100, 149, 237, .7);
            color: aliceblue;
            border-style: hidden;
            border-radius: 5px;
            width: 100px;
            height: 31px;
            font-size: 16px;
        }

        .introduce>textarea {
            background-color: rgba(216, 191, 216, 0.5);
            border-style: hidden;
            outline: none;
            border-radius: 5px;
        }

        h1 {
            text-align: center;
            margin-bottom: 20px;
            margin-top: 20px;
            color: #f0edf3;
        }

        b {
            margin-left: 50px;
            color: #FFEB3B;
            font-size: 10px;
            font-weight: initial;
        }
    </style>
</head>

<body>
<div id="loginDiv">
    <form action="${pageContext.request.contextPath}/user/addUser" method="post" onsubmit="return checkForm()">
        <h1>注册</h1>
        <p>用户名&nbsp;&nbsp;&nbsp;&nbsp;<input id="username" name="username" type="text" autofocus required><label id="name_trip"></label></p>

        <p>账号&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<input id="account" name="account" type="text" required><label id="account_trip"></label></p>

        <p>密码&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<input id="password" name="password" type="password" required><label id="password_trip"></label></p>

        <p>确认密码<input id="surePassword" type="password" required><label id="surePassword_trip"></label></p>

        <%-- 隐藏框,默认提交权限为2,代表普通用户 --%>
        <p><input id="permission" name="permission" type="hidden" value="2"/>

        <br>
        <p style="text-align: center;">
            <input type="submit" class="button" value="提交">
            <input type="reset" class="button" value="重置">
        </p>
    </form>
</div>

</body>
<script>
    function checkForm() {

        //获取用户名输入项
        var username = document.getElementById("username").value;
        if (username.length < 1 || username.length > 10) {
            alert("用户名长度为1-10位!!");
            return false;
        }

        //获取账号输入项
        var account = document.getElementById("account").value;
        if (account.length < 1 || account.length > 10) {
            alert("账号长度为1-10位!!");
            return false;
        }

        //密码长度大于6小于20
        var password = document.getElementById("password").value;
        if (password.length < 6 || password.length > 20) {
            alert("密码长度为6-20位!!");
            return false;
        }

        //获取确认密码框的值 var
        var surePassword = document.getElementById("surePassword").value;
        if (password != surePassword) {
            alert("两次密码不一致");
            return false;
        }

        // 所有输入均合法
        return true;
    }

</script>
</html>

6.6 login.jsp

在jsp文件夹中创建login.jsp文件,作用是提供用户登录界面,并且包含跳转到注册页面的功能(虽然还有个忘记密码,但只是个摆设)

<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" language="java" %>
<html>
<head>
    <meta charset="UTF-8">
    <title>用户登录</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        html {
            height: 100%;
            width: 100%;
            overflow: hidden;
            margin: 0;
            padding: 0;
            background: url("${pageContext.request.contextPath}/images/desert.png") no-repeat 0px 0px;
            background-repeat: no-repeat;
            background-size: 100% 100%;
            -moz-background-size: 100% 100%;
        }
    </style>
</head>
<body>
    <div style="width:400px;height:270px; margin:auto;position:absolute;top:0;bottom:0;left:0;right:0;border:1px solid black;background-color:#87CEEB;">
        <div style="position:absolute;width:400px;height:50px;font-size:30px;background-color:#00BFFF;text-align:center">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</div>
        <div style="position:absolute;left:10px;top:70px;width:100px;height:30px;font-size:25px;">account</div>
        <div style="position:absolute;left:10px;top:120px;width:100px;height:30px;font-size:25px;">password</div>
        <form method="get" action="${pageContext.request.contextPath}/user/login">
            <input type="text" name="account" id="account" style="position:absolute;left:140px;top:70px;width:200px;height:30px;font-size:20px;"><br/>
            <input type="password" name="password" id="password" style="position:absolute;left:140px;top:120px;width:200px;height:30px;font-size:20px;"><br/>
            <input type="submit" value="登    录" style="position:absolute;left:70px;top:170px;width:260px;height:50px;font-size:30px;background-color:#3CB371;">
            <a href="${pageContext.request.contextPath}/user/toAddUser" style="text-decoration:none; position:absolute;left:0px;top:245px;background-color:#87CEEB;border:1px solid">注册新用户 </a>
            <input type="button" value="忘记密码" style="position:absolute;left:320px;top:245px;background-color:#87CEEB;border:none">
        </form>
    </div>
</body>
</html>

6.7 userBooks.jsp

在jsp文件夹中创建userBooks.jsp文件,作用是显示用户所借的书籍,并且提供还书操作

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

<html>
<head>
    <meta charset="UTF-8">
    <title>我的借阅</title>
    <link rel="stylesheet" type="text/css" href="asserts/bootstrap/css/bootstrap.css" />
    <script type="text/javascript" src="${pageContext.request.contextPath}/js/jquery.min.js"></script>
    <style type="text/css">
        html,body{
            width: 100%;
            height: 100%;
        }
        body{
            background-image: url("${pageContext.request.contextPath}/images/userBooks.jpg");
            background-size: 100% 100%;
            width: 100%;
            height: 100%;
            background-repeat: no-repeat;
        }
        table,th,td{
            border: 1px solid red ;
            border-collapse: collapse;
        }
    </style>
</head>
<body>
    <%-- 先取出用户登录信息,只有登录的用户能访问到这里 --%>
    <%
        User user = (User) session.getAttribute("login");
        request.setAttribute("user", user);
    %>
    <h1 style="color: darkorange" align="center">${user.username}借阅的图书</h1>
    <div class="container" style="width: 630px; position:relative; left: 50%; top: 10%; background-color: white">
        <table class="tablelist">
            <thead>
            <tr>
                <th style="width: 50px; text-align: center">序号</th>
                <th style="width: 140px; text-align: center">书名</th>
                <th style="width: 310px; text-align: center">描述</th>
                <th style="width: 100px;text-align: center">操作</th>
            </tr>
            </thead>
            <tbody>
            <%-- 先判断有没有借书 --%>
            <c:if test="${books != null}" >
                <c:forEach items="${books}" var="book" varStatus="st">
                    <tr style="height: 35px">
                        <td align="center">${st.count}</td>
                        <td align="center">${book.bookname}</td>
                        <td align="center">${book.detail}</td>
                        <td align="center">
                            <input type="button" style="border: 1px solid " onclick="returnBook(${book.bookid})" value="还书"/>
                        </td>
                    </tr>
                </c:forEach>
            </c:if>
            </tbody>
        </table>
        <jsp:include page="returnIndex.jsp" />
    </div>

    <script type="text/javascript">
        // 用ajax发送换书请求
        function returnBook(bookid) {
            var userid = ${user.userid};
            var bookid = bookid;
            $.ajax({
                url:"${pageContext.request.contextPath}/book/returnBook",
                type:"post",
                data:JSON.stringify({userid:userid,bookid:bookid}),
                contentType:"application/json;charset=UTF-8",
                success:function (data) {
                    alert(data.info);
                    location.reload();
                }
            });
        }
    </script>
</body>
</html>

6.8 addBook.jsp

在jsp文件夹中创建addBook.jsp文件,提供给管理员添加书籍的功能

<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" language="java" %>
<html>
<head>
    <meta charset="UTF-8">
    <title>添加图书</title>
    <style type="text/css">
        *{
            margin: 0;
            padding: 0;
        }
        html,body{
            width: 100%;
            height: 100%;
        }
        body{
            background-image: url("${pageContext.request.contextPath}/images/bookInfo.jpg");
            background-size: 100% 100%;
            width: 100%;
            height: 100%;
            background-repeat: no-repeat;
            position: absolute;
        }
        input{
            border: #f531ed 1px solid;
            padding-left: 1px;
            width: 300px;
            left:60px;
            position: absolute;
        }
        .input1{
            height: 50px;
            width: 50px;
            left: 350px;
            bottom: 0px;
            margin: 0;
        }
        .input2{
            border: #f531ed 1px solid;
            padding-left: 1px;
            width: 300px;
            height: 150px;
            left:60px;
            position: absolute;
        }
        form{
            height: 100%;
            width: 100%;
            box-sizing: border-box;
            padding-top:15px;
        }
        .p1{
            width: 400px;
            height: 300px;
            left: 38%;
            top: 5%;
            background-color: #ffffff;
            position: relative;
        }
        .text1{
            font-size: 20px;
            color: blueviolet;
            position: absolute;
            top: 0px;
            display: inline-block;
        }
        .p1 div{
            width:400px;
            height: 30px;
            position: relative;
            margin-bottom:10px ;
        }
        .div1{
            height: 50px;
        }
    </style>
</head>
<body>
    <div class="p1">
        <form action="${pageContext.request.contextPath}/book/addBook" method="post" onsubmit="return checkValue()">
            <div>
                <a class="text1"> 名字:</a>
                <input  type="text" name="bookname" id="bookname" placeholder="请给书起个名吧" >
            </div>

            <div>
                <a class="text1"> 数量:</a>
                <input  type="number" name="bookcount", id="bookcount" placeholder="书的数量" >
            </div>

            <div class="div1">
                <a class="text1"> 描述:</a>
                <textarea class="input2" type="text" name="detail" id="detail" placeholder="请用几句话描述此书" ></textarea>
            </div>

            <input class="input1" type="submit" value="添加" >
        </form>
        <jsp:include page="returnIndex.jsp" />
    </div>

    <script>
        <%-- 用一个js函数判断有没有输入书名和数量,描述信息选填(但不能超过100) --%>
        function checkValue() {
            var str = document.getElementById("bookname").value;
            if (str.length < 1 || str.length > 10){
                alert("书名为1-10字");
                document.getElementById("bookname").focus();
                return false;
            }
            str = document.getElementById("bookcount").value;
            if (str.length < 1){
                alert("请输入数量");
                document.getElementById("bookcount").focus();
                return false;
            }
            str = document.getElementById("detail").value;
            if (str.length > 100){
                alert("书籍描述不能超过100字");
                document.getElementById("detail").focus();
                return false;
            }

            return true;
        }
    </script>
</body>
</html>

6.9 updateBook.jsp

在jsp文件夹中创建updateBook.jsp文件,提供给管理员更新图书信息的功能,它大体与addBook.jsp文件一样,只是有些细微的文字不同,同时js函数也不同

<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" language="java" %>
<html>
<head>
    <meta charset="UTF-8">
    <title>更新书籍信息</title>
    <style type="text/css">
        *{
            margin: 0;
            padding: 0;
        }
        html,body{
            width: 100%;
            height: 100%;
        }
        body{
            background-image: url("${pageContext.request.contextPath}/images/bookInfo.jpg");
            background-size: 100% 100%;
            width: 100%;
            height: 100%;
            background-repeat: no-repeat;
            position: absolute;
        }
        input{
            border: #f531ed 1px solid;
            padding-left: 1px;
            width: 300px;
            left:60px;
            position: absolute;
        }
        .input1{
            height: 50px;
            width: 50px;
            left: 350px;
            bottom: 0px;
            margin: 0;
        }
        .input2{
            border: #f531ed 1px solid;
            padding-left: 1px;
            width: 300px;
            height: 150px;
            left:60px;
            position: absolute;
        }
        form{
            height: 100%;
            width: 100%;
            box-sizing: border-box;
            padding-top:15px;
        }
        .p1{
            width: 400px;
            height: 300px;
            left: 38%;
            top: 5%;
            background-color: #ffffff;
            position: relative;
        }
        .text1{
            font-size: 20px;
            color: blueviolet;
            position: absolute;
            top: 0px;
            display: inline-block;
        }
        .p1 div{
            width:400px;
            height: 30px;
            position: relative;
            margin-bottom:10px ;
        }
        .div1{
            height: 50px;
        }
    </style>
</head>
<body>
    <div class="p1">
        <form action="${pageContext.request.contextPath}/book/updateBook" method="post" onsubmit="return checkValue()">
            <input id="bookid" name="bookid" value="${bookid}" type="hidden"/>
            <div>
                <a class="text1"> 名字:</a>
                <input  type="text" name="bookname" id="bookname" placeholder="新的书名" >
            </div>

            <div>
                <a class="text1"> 数量:</a>
                <input  type="number" name="bookcount", id="bookcount" placeholder="新的数量" >
            </div>

            <div class="div1">
                <a class="text1"> 描述:</a>
                <textarea class="input2" type="text" name="detail" id="detail" placeholder="新的描述" ></textarea>
            </div>

            <input class="input1" type="submit" value="修改" >
        </form>
        <jsp:include page="returnIndex.jsp" />
    </div>

    <%-- 判断更新时是否至少输入了一条新的信息 --%>
    <script>
        function checkValue() {
            var str = document.getElementById("bookname").value;
            if (str.length == 0){
                str = document.getElementById("bookcount").value;
                if (str.length == 0){
                    str = document.getElementById("detail").value;
                    if (str.length == 0){
                        alert("请输入至少一项信息");
                        return false;
                    }
                }
            }
            return true;
        }
    </script>
</body>
</body>
</html>

七 启动tomcat进行测试(大家也可以每进行一步操作的时候查看IDEA控制台的输出情况)

7.1 先测试不登录的情况下能访问的资源

①主页面
主页面1
②所有图书界面
所有图书界面1
③如果想要访问其他资源,例如在浏览器上输入http://localhost/library/user/listUserBooks?userid=1,就会被引到登录界面
登录界面1

7.2 用管理员账号进行测试

①添加图书
主页面2
添加图书1
所有图书界面2
②尝试添加同名书籍
11
22
33
③如果添加了错误的书籍,可以点击删除
11
④如果添加的书籍中有些信息需要修改,可以点击修改
1
1
1
⑤如果试图访问用户专属页面,例如http://localhost/library/user/listUserBooks?userid=1,就会被拦截器拦下

7.3 注册一个用户进行测试

1
①先查看我的借阅
2
3
②然后去借几本书,可以尝试借相同的书,借数量为0的书
4
55
6
③回到我的借阅,还几本书
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
④退出登录,测试结束
111

八 尾声

1.至此,该项目就完成了,大家可以往里面加更多的功能去丰富它,可扩展的空间还是很多的。我只是想在学习SSM框架后做个练手项目来巩固所学知识,毕竟实际动手才能记得更牢。在做这个项目的过程中,出现忘记知识点的情况是很正常的,可以经常去翻看笔记、书本等。建议大家能自己思考并动手完成,这样才能将SSM框架的整合开发学到手。之后的话,就一起向springboot进发吧!
2.这个项目完成以及博客的编写不容易,这是本人的第一篇博客,还希望大家多多鼓励,谢谢支持。创作不易,请勿抄袭,转载请注明出处。如果程序或文章写的有什么问题、有什么不懂的地方都可以提问,我会尽我所能回答。

3.项目源码
GitHub
百度网盘 提取码:LXDS
CSDN资源

评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值