Need help with ssm-BookAppointment-master?
Click the “chat” button below for chat support from the developer who created it, or find similar developers for support.

About the developer

jhyscode
141 Stars 49 Forks 4 Commits 2 Opened issues

Description

优雅整合SSM框架:SpringMVC + Spring + MyBatis(图书预约系统)

Services available

!
?

Need anything else?

Contributors list

# 352,534
Java
JavaScr...
4 commits

ssm-BookAppointment

优雅整合SSM框架:SpringMVC + Spring + MyBatis(图书预约系统)
话不多说先让我们看看此项目运行起来的效果,方便大家有个直观了解,好在事先估计要用到那些技术,方便大家有针对性的学习。
看图!
这是图书管理系统的首页,不用登陆即可查看、搜索系统有已有图书。
image 搜索想要预定的书:
image 点击详情,进入预定页面,如果没有登陆的话会弹出登陆层,提示输入学号密码,如果用户已经在之前查看某本书的详细时已经登陆,则不会要求再登陆,这里的用户名密码与数据库验证成功后保存在cookie中,且生命周期默认与session相同,即关闭路径窗口后就失效。
image 这是什么都不输入和输入格式不对时,点击submit的提示:
image image image 登陆后进入预约界面,点击预约后的界面:
image 如果已经对当前书预约过,点击预约会提示重复预约,如果没有库存,点击预约会提示没有库存。
image 预约后点击:查看我的已预约书籍,会显示之前所有预约的信息:
image

1、利用maven创建文件路径:

利用命令行工具输入:mvn archetype:generate -DgroupId=cn.nize -DarchetypeArtifactId= maven-archetype-webapp -DarchetypeCatalog=internal

2、创建项目包:

image

3、再然后就是在pom.xml里面注入依赖,maven会自动在网站上下载。关于maven大家可以看看慕课网上的教学视频。

pom.xml ```java 4.0.0 cn.nize ssm-BookAppointment 1.0-SNAPSHOT ssm-BookAppointment Maven Webapp http://maven.apache.org

    
    
        junit
        junit
        4.11
    

<!-- 1.日志 -->
<!-- 实现slf4j接口并整合 -->
<dependency>
    <groupid>ch.qos.logback</groupid>
    <artifactid>logback-classic</artifactid>
    <version>1.1.1</version>
</dependency>

<!-- 2.数据库 -->
<dependency>
    <groupid>mysql</groupid>
    <artifactid>mysql-connector-java</artifactid>
    <version>5.1.35</version>
    <!--  <scope>runtime</scope> -->
</dependency>
<dependency>
    <groupid>c3p0</groupid>
    <artifactid>c3p0</artifactid>
    <version>0.9.1.2</version>
</dependency>

<!-- DAO: MyBatis -->
<dependency>
    <groupid>org.mybatis</groupid>
    <artifactid>mybatis</artifactid>
    <version>3.3.0</version>
</dependency>
<dependency>
    <groupid>org.mybatis</groupid>
    <artifactid>mybatis-spring</artifactid>
    <version>1.2.3</version>
</dependency>

<!-- 3.Servlet web -->
<dependency>
    <groupid>taglibs</groupid>
    <artifactid>standard</artifactid>
    <version>1.1.2</version>
</dependency>
<dependency>
    <groupid>jstl</groupid>
    <artifactid>jstl</artifactid>
    <version>1.2</version>
</dependency>
<dependency>
    <groupid>com.fasterxml.jackson.core</groupid>
    <artifactid>jackson-databind</artifactid>
    <version>2.5.0</version>
</dependency>
<dependency>
    <groupid>com.fasterxml.jackson.core</groupid>
    <artifactid>jackson-core</artifactid>
    <version>2.5.0</version>
</dependency>
<dependency>
    <groupid>com.fasterxml.jackson.core</groupid>
    <artifactid>jackson-annotations</artifactid>
    <version>2.5.0</version>
</dependency>
<dependency>
    <groupid>javax.servlet</groupid>
    <artifactid>javax.servlet-api</artifactid>
    <version>3.1.0</version>
</dependency>

<!-- 4.Spring -->
<!-- 1)Spring核心 -->
<dependency>
    <groupid>org.springframework</groupid>
    <artifactid>spring-core</artifactid>
    <version>4.1.7.RELEASE</version>
</dependency>
<dependency>
    <groupid>org.springframework</groupid>
    <artifactid>spring-beans</artifactid>
    <version>4.1.7.RELEASE</version>
</dependency>
<dependency>
    <groupid>org.springframework</groupid>
    <artifactid>spring-context</artifactid>
    <version>4.1.7.RELEASE</version>
</dependency>
<!-- 2)Spring DAO层 -->
<dependency>
    <groupid>org.springframework</groupid>
    <artifactid>spring-jdbc</artifactid>
    <version>4.1.7.RELEASE</version>
</dependency>
<dependency>
    <groupid>org.springframework</groupid>
    <artifactid>spring-tx</artifactid>
    <version>4.1.7.RELEASE</version>
</dependency>
<!-- 3)Spring web -->
<dependency>
    <groupid>org.springframework</groupid>
    <artifactid>spring-web</artifactid>
    <version>4.1.7.RELEASE</version>
</dependency>
<dependency>
    <groupid>org.springframework</groupid>
    <artifactid>spring-webmvc</artifactid>
    <version>4.1.7.RELEASE</version>
</dependency>
<!-- 4)Spring test -->
<dependency>
    <groupid>org.springframework</groupid>
    <artifactid>spring-test</artifactid>
    <version>4.1.7.RELEASE</version>
</dependency>

<!-- redis客户端:Jedis -->
<dependency>
    <groupid>redis.clients</groupid>
    <artifactid>jedis</artifactid>
    <version>2.7.3</version>
</dependency>
<dependency>
    <groupid>com.dyuproject.protostuff</groupid>
    <artifactid>protostuff-core</artifactid>
    <version>1.0.8</version>
</dependency>
<dependency>
    <groupid>com.dyuproject.protostuff</groupid>
    <artifactid>protostuff-runtime</artifactid>
    <version>1.0.8</version>
</dependency>

<!-- Map工具类 -->
<dependency>
    <groupid>commons-collections</groupid>
    <artifactid>commons-collections</artifactid>
    <version>3.2</version>
</dependency>

ssm-BookAppointment


<h2>4、做好之前的准备工作后,逻辑理顺,现在就要开始编码啦,首先我们创建数据库!<br>
</h2>

<p>根据我们的实际情况,在数据库中创建表:<br>
schema.sql
``</p>
<pre>java
-- 创建图书表
CREATE TABLE</pre>book<pre>( 
</pre>book<em>id` bigint(20) NOT NULL AUTO</em>INCREMENT COMMENT '图书ID',
  <pre>name</pre> varchar(100) NOT NULL COMMENT '图书名称',
  <pre>introd</pre> varchar(1000) NOT NULL COMMENT '简介',
  <pre>number</pre> int(11) NOT NULL COMMENT '馆藏数量',
  PRIMARY KEY (<pre>book_id</pre>)
) ENGINE=InnoDB AUTO_INCREMENT=1000 DEFAULT CHARSET=utf8 COMMENT='图书表';

<p>-- 初始化图书数据
INSERT INTO </p>
<pre>book</pre> (<pre>book_id</pre>, <pre>name</pre>, <pre>introd</pre>,<pre>number</pre>)
VALUES
    (1000, 'Java程序设计', 'Java程序设计是一门balbal',10),
    (1001, '数据结构','数据结构是计算机存储、组织数据的方式。数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。', 10),
    (1002, '设计模式','设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。',10),
    (1003, '编译原理','编译原理是计算机专业的一门重要专业课,旨在介绍编译程序构造的一般原理和基本方法。',10),
    (1004,'大学语文','基于长期的教学实践和学科思考,我们编写了这本《大学语文》教材balbal',10),
    (1005,'计算方法','计算方法又称“数值分析”。是为各种数学问题的数值解答研究提供最有效的算法。',10),
    (1006,'高等数学','广义地说,初等数学之外的数学都是高等数学,也有将中学较深入的代数、几何以及简单的集合论初步balbal',10);

<p>-- 创建预约图书表
CREATE TABLE </p>
<pre>appointment</pre> (
  <pre>book_id</pre> bigint(20) NOT NULL COMMENT '图书ID',
  <pre>student_id</pre> bigint(20) NOT NULL COMMENT '学号',
  <pre>appoint_time</pre> timestamp NOT NULL DEFAULT CURRENT<em>TIMESTAMP ON UPDATE CURRENT</em>TIMESTAMP COMMENT '预约时间' ,
  PRIMARY KEY (<pre>book_id</pre>, <pre>student_id</pre>),
  INDEX <pre>idx_appoint_time</pre> (<pre>appoint_time</pre>)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='预约图书表';
-- 创建学生数据表
CREATE TABLE <pre>student</pre>(
    <pre>student_id</pre> bigint(20) NOT NULL COMMENT '学生ID',
    <pre>password</pre>  bigint(20) NOT NULL COMMENT '密码',
    PRIMARY KEY(<pre>student_id</pre>)
)ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='学生统计表';<br>
-- 初始化学生数据
INSERT INTO <pre>student</pre>(<pre>student_id</pre>,<pre>password</pre>) 
VALUES
    (3211200801,346543),
    (3211200802,754323),
    (3211200803,674554),
    (3211200804,542344),
    (3211200805,298383),
    (3211200806,873973),
    (3211200807,193737),
    (3211200808,873092);
<pre>## 5 根据数据库对象创建实体类。<br>
第一个开始填充的类当然是entiy包,他是承接我们从数据库里去除数据的类,或者把该类存入数据库,在这里我们创建两个类,一个是Book包(从数据库取出书后放入该包),一个是Appointment(存放从数据库取出的预约书籍信息)。<br>
Book.java
```java
package com.imooc.appoint.entiy;

public class Book {

    private long bookId;//图书ID
    private String name;//图书名称
    private int number;//馆藏数量 
    private String introd;
    public Book() {  //为什么要有个无参构造器
    }
    public Book(long bookId, String name, int number) { 
        this.bookId = bookId;
        this.name = name;
        this.number = number;
    }
    public long getBookId() {
        return bookId;
    }
    public void setBookId(long bookId) {
        this.bookId = bookId;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getNumber() {
        return number;
    }
    public void setNumber(int number) {
        this.number = number;
    }
    public String getIntrod() {
        return introd;
    }
    public void setIntrod(String introd) {
        this.introd = introd;
    }

    @Override
    public String toString() {
        return "Book [bookId=" + bookId + ", name=" + name + ", number=" + number + ", introd=" + introd + "]";
    }
}
</pre>
<p>Appointment.java
```java
package com.imooc.appoint.entiy;</p>

<p>import java.util.Date; </p>

<p>public class Appointment {
    private long bookId;
    private long studentId;
    private Date appointTime;
    // 多对一的复合属性
    private Book book;// 图书实体
    public Appointment(){
    }
    public Appointment(long bookId, long studentId, Date appointTime) { 
        this.bookId = bookId;
        this.studentId = studentId;
        this.appointTime = appointTime;
    }
    public long getBookId() {
        return bookId;
    }
    public void setBookId(long bookId) {
        this.bookId = bookId;
    }
    public long getStudentId() {
        return studentId;
    }
    public void setStudentId(long studentId) {
        this.studentId = studentId;
    }
    public Date getAppointTime() {
        return appointTime;
    }
    public void setAppointTime(Date appointTime) {
        this.appointTime = appointTime;
    }
    public Book getBook() {
        return book;
    }
    public void setBook(Book book) {
        this.book = book;
    }
    @Override
    public String toString() {
        return "Appointment [bookId=" + bookId + ", studentId=" + studentId + ", appointTime=" + appointTime + ", book="
                + book + "]";
    }
}
</p>
<pre>
因为我们还打算做一个学生ID、密码验证,所以也得有学生类.<br>
Student.java
</pre>java
package com.imooc.appoint.entiy;

<p>public class Student {<br>
    private Long studentId;
    private Long password;</p>
<pre>public Student(){

}
public Student(Long studentId,Long password){
    this.studentId=studentId;
    this.password=password;
}
public Long getStudentId() {
    return studentId;
}
public void setStudentId(Long studentId) {
    this.studentId = studentId;
}
public Long getPassword() {
    return password;
}
public void setPassword(Long password) {
    this.password = password;
}

@Override
public String toString() {
    return "Student [studentId=" + studentId + ", password=" + password + "]";
}
</pre>
<p>}</p>
<pre>## 6创建DAO中的类<br>
DAO包中的类主要功能是实现与数据库交互,所以我们需要大的逻辑是:1、学生类与数据库交互;2、预约是与数据库交互;3、查询书时与时与数据库交互。这里大致分为几类,等具体写接口时再去细写。还有就是注意,DAO只是与数据库交互,不能写交互组成的逻辑,这个放在service里面。我们应该站在用户的角度设计DAO接口,至于具体怎么用,后面再写。<br>
BookDao.java
```java
package com.imooc.appoint.dao;

import java.util.List;

import org.apache.ibatis.annotations.Param;

import com.imooc.appoint.entiy.Book;

public interface BookDao {
    /*
     * 根据id查询书
     * 
     */
    Book queryById(long id);
    List<book> querySome(String name);
    List<book> queryAll(@Param("offset") int offset,@Param("limit") int limit);

    /*减少管存数量
     * 用返回值可判断当前库存是否还有书
     */
     int reduceNumber(long bookId);
}


</book></book></pre>
<p>AppointmentDao.java
```java
package com.imooc.appoint.dao;</p>

<p>import java.util.List;</p>

<p>import org.apache.ibatis.annotations.Param;</p>

<p>import com.imooc.appoint.entiy.Appointment; </p>

<p>public interface AppointmentDao {
    //通过图书ID和学生ID预约书籍,并插入
    int insertAppointment(@Param("bookId") long bookId, @Param("studentId") long studentId);</p>
<pre>//通过一个学生ID查询已经预约了哪些书。
List<appointment> quaryAndReturn(long studentId);
</appointment></pre>
<p>}</p>
<pre>StudentDao.java
```java
package com.imooc.appoint.dao;

import org.apache.ibatis.annotations.Param;

import com.imooc.appoint.entiy.Student;

public interface StudentDao {
    /**
     * 向数据库验证输入的密码是否正确
     */
    Student quaryStudent(@Param("studentId") long studentId, @Param("password") long password);
}

</pre>
<h2>7我们暂时先不管以上接口实现,先为DAO配置入框架,实现spring与mybatis的整合。<br>
</h2>

<p>因为spring的配置太多,我们这里分三层,分别是dao service web。我们这里先写spring-dao.xml,其他的等我们实现相应包后再去配置。
 配置时,主要注意一下几个方面:<br>
1、读入数据库连接相关参数(可选)<br>
2、配置数据连接池<br>
3、配置连接属性,可以不读配置项文件直接在这里写死<br>
4、配置c3p0,只配了几个常用的<br>
5、配置SqlSessionFactory对象(mybatis)<br>
6、扫描dao层接口,动态实现dao接口,也就是说不需要daoImpl,sql和参数都写在xml文件上<br>
spring-dao.xml
```java
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
<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.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">
            &lt;!-- 配置整合mybatis过程 --&gt;
    &lt;!-- 1.配置数据库相关参数   properties的属性:${url} --&gt;
    <property-placeholder location="classpath:jdbc.properties"></property-placeholder></beans></p>
<pre><!-- 2.数据库连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <!-- 配置连接池属性 -->
    <property name="driverClass" value="${jdbc.driver}"></property>
    <property name="jdbcUrl" value="${jdbc.url}"></property>
    <property name="user" value="${jdbc.username}"></property>
    <property name="password" value="${jdbc.password}"></property>

    <!-- c3p0连接池的私有属性 -->
    <property name="maxPoolSize" value="30"></property>
    <property name="minPoolSize" value="10"></property>
    <!-- 关闭连接后不自动commit -->
    <property name="autoCommitOnClose" value="false"></property>
    <!-- 获取连接超时时间 -->
    <property name="checkoutTimeout" value="10000"></property>
    <!-- 当获取连接失败重试次数 -->
    <property name="acquireRetryAttempts" value="2"></property>
</bean>
    <!-- 3.配置SqlSessionFactory对象 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <!-- 注入数据库连接池 -->
    <property name="dataSource" ref="dataSource"></property>
    <!-- 配置MyBaties全局配置文件:mybatis-config.xml -->
    <property name="configLocation" value="classpath:mybatis-config.xml"></property>
    <!-- 扫描entity包 使用别名 -->
    <property name="typeAliasesPackage" value="com.imooc.appoint.entity"></property> 
    <!-- 扫描sql配置文件:mapper需要的xml文件 -->
    <property name="mapperLocations" value="classpath:mapper/*.xml"></property>
</bean>

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

<h3>配置jdbc.properties文件<br>
</h3>
<pre class="language-java">jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/ssmbookappoint?useUnicode=true&amp;characterEncoding=utf8
jdbc.username= 
jdbc.password= 
</pre>
<p>第一次上传忘记去掉用户名密码了,好险!大家用的时候填上自己的用户名、密码。<br></p>

<h3>配置mybatis。<br>
</h3>

<p>所以需要配置mybatis核心文件,在recources文件夹里新建mybatis-config.xml文件。<br></p>

<p>1、使用自增主键<br>
2、使用列别名<br>
3、开启驼峰命名转换 create_time -&gt; createTime<br>
mybatis-config.xml
```java
&lt;?xml version="1.0" encoding="UTF-8" ?&gt;
&lt;!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd"&gt;
<configuration>
    &lt;!-- 配置全局属性 --&gt;
    <settings>
        &lt;!-- 使用jdbc的getGeneratedKeys获取数据库自增主键值 --&gt;
        <setting name="useGeneratedKeys" value="true"></setting></settings></configuration></p>
<pre>    <!-- 使用列别名替换列名 默认:true -->
    <setting name="useColumnLabel" value="true"></setting>

    <!-- 开启驼峰命名转换:Table{create_time} -> Entity{createTime} -->
    <setting name="mapUnderscoreToCamelCase" value="true"></setting>

</pre>
<p>
```</p>

<h2>8配置实现接口的xml<br>
</h2>

<p>我们要开始写DAO接口的实现类,因为我们配置扫描sql配置文件路径是:mapper下的xml,所有我们应该在resourse下新建mapper文件夹,在这里存放实现DAO接口的各种xml<br>
BookDao.xml
```java
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;!DOCTYPE mapper
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd"&gt;
<mapper namespace="com.imooc.appoint.dao.BookDao">
    <select id="queryById" parametertype="long" resulttype="com.imooc.appoint.entiy.Book">
        &lt;!-- 具体的sql --&gt;
        SELECT
            book<em>id,
            name,
            introd,
            number
        FROM
            book
        WHERE
            book</em>id = #{bookId}
    </select></mapper></p>
<pre><select id="querySome" parametertype="com.imooc.appoint.entiy.Book" resulttype="com.imooc.appoint.entiy.Book">
    SELECT book_id,name,introd,number FROM book
    <where>
        <!-- <if test="name !=null and !&quot;&quot;.equals(name.trim())">  -->
            and name like '%' #{name} '%'
        <!--  </if>   -->
    </where> 
</select> 

<select id="queryAll" resulttype="com.imooc.appoint.entiy.Book">
    SELECT
        book_id,
        name,
        introd,
        number
    FROM
        book
    ORDER BY
        book_id
    LIMIT #{offset}, #{limit}
</select>

<update id="reduceNumber">
    UPDATE book
    SET number = number - 1
    WHERE
        book_id = #{bookId}
    AND number &gt; 0
</update>
</pre>
<p>
</p>
<pre>
AppointmentDao.xml
</pre>java
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;!DOCTYPE mapper
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd"&gt;
    <mapper namespace="com.imooc.appoint.dao.AppointmentDao">
    <insert id="insertAppointment">
        &lt;!-- ignore 主键冲突,报错 --&gt;
        INSERT ignore INTO appointment (book<em>id, student</em>id)
        VALUES (#{bookId}, #{studentId})
    </insert>
&lt;!--     <delete id="deleteOne">
        DELETE FROM appointment WHERE book<em>id=#{bookId} AND student</em>id=#{studentId} 
    </delete>  用不到这个功能--&gt;
    &lt;!-- //查询某个学生的所有预约记录 --&gt;
    <select id="quaryAndReturn" resulttype="com.imooc.appoint.entiy.Appointment">
        &lt;!-- 如何告诉MyBatis把结果映射到Appointment同时映射book属性 --&gt;
        &lt;!-- 可以自由控制SQL --&gt;
        SELECT
            a.book<em>id,&lt;!--mybatis会认为是book</em>id,又因为开启了驼峰命名法 所以最终是bookId --&gt;
            a.student<em>id,
            a.appoint</em>time,
            b.book<em>id "book.book</em>id",&lt;!--b.book<em>id as "book.book</em>id" 告诉mybatis b.book<em>id是Appointment中book属性的值--&gt;
            b.<pre>name</pre> "book.name",
            b.introd "book.introd",
            b.number "book.number"
        FROM
            appointment a
        INNER JOIN book b ON a.book</em>id = b.book<em>id
        WHERE 
            a.student</em>id = #{studentId}
    </select>
&lt;!--    //查询所有被预约了的书
    <select id="queryAll">
        SELECT 
            a.book<em>id, 
            a.student</em>id,
            a.appoint<em>time, 
            b.book</em>id "book.book<em>id", 
            b.<pre>name</pre> "book.name",
            b.introd "book.introd"
            b.number "book.number"
        FROM 
            appointment a
        INNER JOIN book b ON a.book</em>id = b.book<em>id<br>
        ORDER BY
            a.book</em>id
        LIMIT #{offset}, #{limit}
    </select> 暂时用不到这个功能,管理员界面待开发--&gt;

<p></p></mapper>
<pre>
StudentDao.java
</pre>java
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;!DOCTYPE mapper
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd"&gt;
<mapper namespace="com.imooc.appoint.dao.StudentDao">
    <select id="quaryStudent" resulttype="com.imooc.appoint.entiy.Student">
        SELECT 
            student<em>id,
            password
        FROM
            student
        WHERE
            student</em>id=#{studentId}
        AND password=#{password}<br>
    </select>

<p></p></mapper>

到此为止,我们dao层的开发就算结束了,大家可以写个测试类,测试一下。(我没写,哈哈),要实现预约功能还得有更多详细的逻辑组织,这个功能将他放在service中,他把DAO中与数据库交互的功能组织成可以用的详细逻辑。提供给上层web调用。

8 service层

首先写service接口类

BookService.java ```java package com.imooc.appoint.service;

import java.util.List;

import com.imooc.appoint.dto.AppointExecution; import com.imooc.appoint.entiy.Appointment; import com.imooc.appoint.entiy.Book; import com.imooc.appoint.entiy.Student;

public interface BookService { /** * 查询一本图书 * * @param bookId * @return / Book getById(long bookId);
/
* * 查询所有图书 * * @return / List getList(); /* * 登陆时查询数据库是否有该学生记录。 / Student validateStu(Long studentId,Long password); /按照图书名称查询 * 按条件搜索图书 * */ List getSomeList(String name); / * 查看某学生预约的所有图书 * / List getAppointByStu(long studentId); /* * 预约图书 * * @param bookId * @param studentId * @return */ AppointExecution appoint(long bookId, long studentId);//返回预约成功的实体类 }

大家可以看到,这个借口类中基本和DAO中的没啥区别,有区别的是某些类他是在dao上更进一步,需要多个dao类一起组织,或者在加入其它逻辑才能实现
为了实现BookService的借口,我们得写BookServiceImpl类。但是想让我们想想,为了写BookServiceImpl,我们需要什么,上面我们已经写出预约成功的实体类是AppointExecution,所以当然我们得写出该类,因为该类交互service和web,对这类有点像entiy包我们管叫bto包(bto包和其他包一起存放在appoint下)。对于AppointExecution来说,作用就是预约成功后给web层提供返回的信息。(就是返回预约成功、预约失败、无库存、之类的信息)
java package com.imooc.appoint.dto; import com.imooc.appoint.enums.AppointStateEnum;

public class AppointExecution {

// 图书ID
private long bookId;

// 预约结果状态 private int state;

// 状态标识 private String stateInfo;

public AppointExecution() { }

// 预约失败的构造器 public AppointExecution(long bookId, AppointStateEnum stateEnum) { this.bookId = bookId; this.state = stateEnum.getState(); this.stateInfo = stateEnum.getStateInfo(); }

//set get 方法! public long getBookId() { return bookId; }

public void setBookId(long bookId) { this.bookId = bookId; }

public int getState() { return state; }

public void setState(int state) { this.state = state; }

public String getStateInfo() { return stateInfo; }

public void setStateInfo(String stateInfo) { this.stateInfo = stateInfo; }

@Override public String toString() { return "AppointExecution [bookId=" + bookId + ", state=" + state + ", stateInfo=" + stateInfo+"]"; }

}

除此之外,我们在预约图书时可能出现异常,例如重复预约、无库存、和其他异常,我们需要事先设计好异常类,来接收这类异常,方便处理,而不是直接报错。因此在appoint包下新建excption包,报下新建三个类:AppoinException.java;NoNumberException.java;RepeatAppoint.java
AppoinException.java
java package com.imooc.appoint.exception; //预约义务异常 public class AppointException extends RuntimeException{ public AppointException(String message) { super(message); }
public AppointException(String message, Throwable cause) {
    super(message, cause);
}

}

NoNumberException.java
```java
package com.imooc.appoint.exception;

//库存不足异常 public class NoNumberException extends RuntimeException{ public NoNumberException(String message) { super(message); }

public NoNumberException(String message, Throwable cause) {
    super(message, cause);
}

}

RepeatAppoint.java ```java package com.imooc.appoint.exception; //重复预约异常 public class RepeatAppointException extends RuntimeException{ public RepeatAppointException(String message) { super(message); }

public RepeatAppointException(String message, Throwable cause) {
    super(message, cause);
}

}

现在可以写接口的实现类啦:BookServiceImpl.java
java package com.imooc.appoint.service.Impl;

import java.util.List;

import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional;

import com.imooc.appoint.dao.AppointmentDao; import com.imooc.appoint.dao.BookDao; import com.imooc.appoint.dao.StudentDao; import com.imooc.appoint.dto.AppointExecution; import com.imooc.appoint.entiy.Appointment; import com.imooc.appoint.entiy.Book; import com.imooc.appoint.entiy.Student; import com.imooc.appoint.enums.AppointStateEnum; import com.imooc.appoint.exception.AppointException; import com.imooc.appoint.exception.NoNumberException; import com.imooc.appoint.exception.RepeatAppointException; import com.imooc.appoint.service.BookService;

@Service public class BookServiceImpl implements BookService{ private Logger logger=LoggerFactory.getLogger(this.getClass()); @Autowired private BookDao bookDao; @Autowired private AppointmentDao appointmentDao; @Autowired private StudentDao studentDao; @Override public Book getById(long bookId) { return bookDao.queryById(bookId); }
@Override public List getList() { return bookDao.queryAll(0, 1000); } @Override public Student validateStu(Long studentId,Long password){ return studentDao.quaryStudent(studentId, password); } @Override public List getSomeList(String name) {

    return bookDao.querySome(name);
} 
@Override
public List getAppointByStu(long studentId) {//DOTO
    return appointmentDao.quaryAndReturn(studentId);

} @Override @Transactional public AppointExecution appoint(long bookId, long studentId) {//在Dao的基础上组织逻辑,形成与web成交互用的方法 try{ //返回成功预约的类型。
int update=bookDao.reduceNumber(bookId);//减库存 if(update<=0){//已经无库存! throw new NoNumberException("no number"); }else{ //执行预约操作 int insert=appointmentDao.insertAppointment(bookId, studentId); if(insert<=0){//重复预约 throw new RepeatAppointException("repeat appoint"); }else{//预约成功 return new AppointExecution(bookId,AppointStateEnum.SUCCESS); }

    }
} catch (NoNumberException e1) {
    throw e1;
} catch (RepeatAppointException e2) {
    throw e2;
} catch (Exception e) {
    logger.error(e.getMessage(), e);
    // 所有编译期异常转换为运行期异常
    throw new AppointException("appoint inner error:" + e.getMessage());
}

}

}

### 又到了我们写service层配置的时候!
该xml依然位于resourse下的spring包下
spring-service.xml ```java

9web层

现在我们一起看看web层该如何组织。
在写具体的代码之前,先配置web层,在这里主要实现的作用是:
1、开启SpringMVC注解模式,可以使用@RequestMapping,@PathVariable,@ResponseBody等
2、对静态资源处理,如js,css,jpg等
3、配置jsp 显示ViewResolver,例如在controller中某个方法返回一个string类型的"login",实际上会返回"/WEB-INF/login.jsp"
4、扫描web层 @Controller
详细见xml中每一步的注释
spring-web.xml
```java <?xml version="1.0" encoding="UTF-8"?> <!-- 配置SpringMVC --> <!-- 1.开启SpringMVC注解模式 --> <!-- 简化配置: (1)自动注册DefaultAnootationHandlerMapping,AnotationMethodHandlerAdapter (2)提供一些列:数据绑定,数字和日期的format @NumberFormat, @DateTimeFormat, xml,json默认读写支持 --> <!-- 2.静态资源默认servlet配置 (1)加入对静态资源的处理:js,gif,png (2)允许使用"/"做整体映射 --> mvc:default-servlet-handler/ <!-- 3.配置jsp 显示ViewResolver -->

 
 

当然我们必须修改web.xml文件,它在webapp的WEB-INF下,这个文件配置servlet,并初始化,指定扫描spring相关的配置,最后我们将所有的url都拦截下来。
web.xml
java BookAppointment-dispatcher org.springframework.web.servlet.DispatcherServlet contextConfigLocation classpath:spring/spring-*.xml BookAppointment-dispatcher /

因为在运行时,我们经常会查看日志什么的,所以我们把这个配置进去
logback.xml ```java %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
<root level="debug">
    <appender-ref ref="STDOUT"></appender-ref>
</root>

最后我们开始组织我们web的逻辑,也就是具体的controller层代码的编写。
这一层中与我们的前段联系的较为紧密,大家可以边写边看效果,再调试。 ```java package com.imooc.appoint.web;

import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map;

import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;

import org.apache.ibatis.annotations.Param; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody;

import com.imooc.appoint.dto.AppointExecution; import com.imooc.appoint.dto.Result; import com.imooc.appoint.entiy.Appointment; import com.imooc.appoint.entiy.Book; import com.imooc.appoint.entiy.Student; import com.imooc.appoint.enums.AppointStateEnum; import com.imooc.appoint.service.BookService; import com.imooc.appoint.exception.RepeatAppointException; import com.imooc.appoint.exception.NoNumberException;

@Controller @RequestMapping("/books") public class BookController { private Logger logger=LoggerFactory.getLogger(this.getClass()); @Autowired private BookService bookService; //获取图书列表 @RequestMapping(value="/list",method = RequestMethod.GET) private String List(Model model){ List list = bookService.getList(); model.addAttribute("list", list);

    return "list";
}
//搜索是否有某图书
@RequestMapping(value="/search",method = RequestMethod.POST)
private void  search(HttpServletRequest req,HttpServletResponse resp) 
                            throws ServletException, IOException{
    //接收页面的值
    String name=req.getParameter("name");
    name=name.trim();
    //向页面传值
    req.setAttribute("name", name);
    req.setAttribute("list", bookService.getSomeList(name)); 
    req.getRequestDispatcher("/WEB-INF/jsp/list.jsp").forward(req, resp); 
}
//查看某图书的详细情况
@RequestMapping(value = "/{bookId}/detail", method = RequestMethod.GET)
private String detail(@PathVariable("bookId") Long bookId, Model model){
    if(bookId==null){
        return "redict:/book/list";
    }
    Book book=bookService.getById(bookId);
    if(book==null){
        return "forward:/book/list"; 
    }
    model.addAttribute("book",book);
    System.out.println(book);
    return "detail";
}
//验证输入的用户名、密码是否正确
@RequestMapping(value="/verify", method = RequestMethod.POST, produces = {
    "application/json; charset=utf-8" })
@ResponseBody
private Map validate(Long studentId,Long password){   //(HttpServletRequest req,HttpServletResponse resp){
    Map resultMap=new HashMap(); 
    Student student =null;  
    System.out.println("验证函数"); 
    student =bookService.validateStu(studentId,password);

System.out.println("输入的学号、密码:"+studentId+":"+password);
System.out.println("查询到的:"+student.getStudentId()+":"+student.getPassword());

if(student!=null){
    System.out.println("SUCCESS");
    resultMap.put("result", "SUCCESS");
    return resultMap;
}else{ 
    resultMap.put("result", "FAILED");
    return resultMap;
}

} //执行预约的逻辑 @RequestMapping(value = "/{bookId}/appoint", method = RequestMethod.POST, produces = { "application/json; charset=utf-8" }) @ResponseBody private Result execute(@PathVariable("bookId") Long bookId,@RequestParam("studentId") Long studentId){ Result result; AppointExecution execution=null;

try{//手动try catch,在调用appoint方法时可能报错
    execution=bookService.appoint(bookId, studentId);
    result=new Result<appointexecution>(true,execution); 
        return result; 

} catch(NoNumberException e1) {
    execution=new AppointExecution(bookId,AppointStateEnum.NO_NUMBER);
    result=new Result<appointexecution>(true,execution);
        return result;
}catch(RepeatAppointException e2){
    execution=new AppointExecution(bookId,AppointStateEnum.REPEAT_APPOINT);
    result=new Result<appointexecution>(true,execution);
        return result;
}catch (Exception e){
    execution=new AppointExecution(bookId,AppointStateEnum.INNER_ERROR); 
    result=new Result<appointexecution>(true,execution);
        return result;
} 

} @RequestMapping(value ="/appoint") private String appointBooks(@RequestParam("studentId") long studentId,Model model){

List<appointment> appointList=new ArrayList<appointment>();
appointList=bookService.getAppointByStu(studentId);
model.addAttribute("appointList", appointList);

return "appointBookList";

}

}

为了把执行预约逻辑是否返回的不同信息封装起来,我们创建一个Result类,它是类型T的集合。
Result.java ```java package com.imooc.appoint.dto; /** * 封装json对象,所有返回结果都使用它 */ public class Result { private boolean success;// 是否成功标志
private T data;// 成功时返回的数据

private String error;// 错误信息

public Result() {
}

// 成功时的构造器
public Result(boolean success, T data) {
    this.success = success;
    this.data = data;
}

// 错误时的构造器
public Result(boolean success, String error) {
    this.success = success;
    this.error = error;
}


public boolean isSuccess() {
    return success;
}

public void setSuccess(boolean success) {
    this.success = success;
}

public T getData() {
    return data;
}

public void setData(T data) {
    this.data = data;
}

public String getError() {
    return error;
}

public void setError(String error) {
    this.error = error;
}

}

为了给web显示预约后的反馈信息,我们建立一个常量数据字典类存放这些要反馈给客户的信息
AppointStateEnum.java ```java package com.imooc.appoint.enums;

//使用枚举表述常量数据字典,我们先定义几个预约图书操作返回码的数据字典,也就是我们要返回给客户端的信息。 public enum AppointStateEnum { SUCCESS(1, "预约成功"), NONUMBER(0, "库存不足"), REPEATAPPOINT(-1, "重复预约"), INNER_ERROR(-2, "系统异常");

private int state;

private String stateInfo;

private AppointStateEnum(int state, String stateInfo) { this.state = state; this.stateInfo = stateInfo; }

public int getState() { return state; }

public String getStateInfo() { return stateInfo; }

public static AppointStateEnum stateOf(int index) { for (AppointStateEnum state : values()) { if (state.getState() == index) { return state; } } return null; }

}

到此为止,我们后端写的差不多了,下面是前段页面的开发,其实前段和controller中的方法开发是相互嵌套的,在写方法的同时,写js或者jsp文件。这样逻辑才能连贯。再此用bootstrap这种轻量一站式框架开发前段,能死前端技术不怎么好的童鞋也能开发出还看得过去的页面。
话不多少,我们上代码:
list.jsp
java <%@page contentType="text/html; charset=UTF-8" language="java" %> <%@include file="common/tag.jsp"%> <!DOCTYPE html>
<title>图书列表</title>
&lt;%@include file="common/head.jsp" %&gt;

图书列表

图书名称:
    
图书ID 图书名称 馆藏数量 详细
${sk.bookId} ${sk.name} ${sk.number} 详细

<!-- jQuery文件。务必在bootstrap.min.js 之前引入 -->

detail.jsp
java <%@page contentType="text/html; charset=UTF-8" language="java" %> <%@include file="common/tag.jsp" %> <!DOCTYPE html>
<title>预约详情页</title>
&lt;%@include file="common/head.jsp" %&gt;

图书详情


图书ID 图书名称 图书简介 剩余数量 预约数量
${book.bookId} ${book.name} ${book.introd} ${book.number } 1


<!--用来展示预约控件--> <!--在js里面调用这个id还可以动态显示一些其他东西,例如动态时间等(需要插件)-->
                查看我的已预约书籍
            

<!-- 登录弹出层 输入电话 -->

<%--jQery文件,务必在bootstrap.min.js之前引入--%> <%--使用CDN 获取公共js http://www.bootcdn.cn/--%> <%--jQuery Cookie操作插件--%> <%--jQuery countDown倒计时插件--%>

bookappointment.js
javascript var bookappointment={ //封装相关ajax的url URL:{ appoint:function(bookId,studentId){ return '/books/'+bookId+'/appoint?studentId='+studentId; }, verify:function(){ return '/books'+'/verify'; } },
//验证学号和密码
validateStudent:function(studentId,password){
    console.log("studentId"+studentId);
    if(!studentId||!password){
        return "nothing";
    }else if(studentId.length!=10 ||isNaN(studentId)||password.length!=6 ||isNaN(password)){
        return "typerror";
    }else {
        if(bookappointment.verifyWithDatabase(studentId, password)){
            console.log("验证成功!");
            return "success";
        }else{
            console.log("验证失败!");
            return "mismatch";
        }
    }  
},
//将学号和用户名与数据库匹配
verifyWithDatabase:function(studentId,password){
    var result=false;
    var params={};
    params.studentId=studentId;
    params.password=password;
    console.log("params.password:"+params.password);
    var verifyUrl=bookappointment.URL.verify();
    $.ajax({
        type:'post',
        url:verifyUrl,
        data:params,
        datatype:'josn', 
        async:false,                       //同步调用,保证先执行result=true,后再执行return result;
        success:function(data){
            if(data.result=='SUCCESS'){
                window.location.reload();
                //弹出登录成功!
                alert("登陆成功!");
                result=true;
            }else{
                result=false;
            }
        }
    });
    console.log("我是验证结果:"+result);
    return result;

},

//预定图书逻辑 detail:{ //预定也初始化 init:function(params){ var bookId=params['bookId']; console.log("我是js文件!");

    var studentId=$.cookie('studentId');
    var password=$.cookie('password');
    if(!studentId||!password){
        //设置弹出层属性
        var  IdAndPasswordModal=$('#varifyModal');
        IdAndPasswordModal.modal({
            show: true,//显示弹出层
                    backdrop: 'static',//禁止位置关闭
                    keyboard: false//关闭键盘事件
        });
        $('#studentBtn').click(function (){
            studentId=$('#studentIdKey').val();
                console.log("studentId:"+studentId);
            password=$('#passwordKey').val();
                console.log("password:"+password);
            //调用validateStudent函数验证用户id和密码。
            var temp=bookappointment.validateStudent(studentId,password);
                console.log(temp);
            if(temp=="nothing"){
                $('#studentMessage').hide().html('<label class="label label-danger">学号或密码为空!</label>').show(300);
            }else if(temp=="typerror"){
                $('#studentMessage').hide().html('<label class="label label-danger">格式不正确!</label>').show(300);
            }else if(temp=="mismatch"){
                console.log("已经调用验证函数!");
                $('#studentMessage').hide().html('<label class="label label-danger">学号密码不匹配!</label>').show(300);
            }else if(temp=="success"){
                //学号与密码匹配正确,将学号密码保存在cookie中。不设置cookie过期时间,这样即为session模式,关闭浏览器就不保存密码了。
                $.cookie('studentId', studentId, {  path: '/books'}); 
                $.cookie('password', password, {  path: '/books'}); 
                    // 跳转到预约逻辑 
                var appointbox=$('#appoint-box');
                bookappointment.appointment(bookId,studentId,appointbox);
            }
        }); 
    }else{
        var appointbox=$('#appoint-box');
        bookappointment.appointment(bookId,studentId,appointbox);
    } 
}   

}, appointment:function(bookId,studentId, node){ console.log("我执行预约的方法!" ); node.html('预约');

var appointmentUrl = bookappointment.URL.appoint(bookId,studentId);
console.log("appointmentUrl:"+appointmentUrl);
//绑定一次点击事件
$('#appointmentBtn').one('click', function () {
    //执行预约请求
    //1、先禁用请求
    $(this).addClass('disabled');
    //2、发送预约请求执行预约
    $.post(appointmentUrl,{},function(result){   //Ajax强大之处,向Controller方法提出请求和返回结果在一处!
        if(result &amp;&amp; result['success']){         //同时还可以连续取对象的子对象!
            var appointResult=result['data'];
                console.log("appointResult"+appointResult);
            var state=appointResult['state'];
                console.log("state"+state);
            var stateInfo=appointResult['stateInfo'];
                console.log("stateInfo"+stateInfo);
            //显示预约结果    把结果显示给页面,完成了jsp的工作 
            node.html('<span class="label label-success">'+stateInfo+'</span>');
        }       //因为公用一个node所以,用来显示“stateInfo”时就不会显示上面的“预约”
        console.log('result'+result);
    });
 });

}

}

appointBookList.jsp
java <%@page contentType="text/html; charset=UTF-8" language="java" %> <%@include file="common/tag.jsp"%> <!DOCTYPE html>
<title>预约图书列表</title>
&lt;%@include file="common/head.jsp" %&gt;

您已预约图书列表


/c:forEach
预定学号 预约时间 图书ID 图书名称 图书简介 预约数量
${sk.studentId} ${sk.appointTime} ${sk.bookId} ${sk.book.getName()} ${sk.book.getIntrod()} 1
<!-- jQuery文件。务必在bootstrap.min.js 之前引入 -->

``` 到此为止,所有的开发就已经结束,已经把所有源代码上传至github,需要的可以去下载,喜欢就给个star吧。

We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.