实验四:图书管理系统案例分析

一、相关知识点

  1. java基础知识
  2. Eclipse环境的基本配置
  3. 数据库基础知识
  4. JDBC基本概念
  5. java连接数据库的方式
  6. JDBC简单查询

二、实验目的:

​ 分析图书管理系统的组成部分,理解其数据库设计和程序模块;在教师指导下阅读各模块的程序,理解持久数据、内存数据、感官数据的基本转换方式。

​ 理解Java连接数据库的基本概念。理解JDBC的四种驱动程序,掌握纯java驱动。理解Statement对象和ResultSet对象。

三、实验内容:

1、 图书管理系统数据库实施:参考讲义中的实施过程。

A、用表格形式编写数据库表的设计,表格格式如下。

  • beanbook

image-20220502161200318

字段名中文名称数据类型能否为空说明
barcode条形码varchar(20)主码
bookname书名varchar(200)
pubid出版社IDvarchar(20)外码,默认为空,拥有名为fk_pubid_idx的普通索引,拥有名为fk_pubid的约束,不允许beanpublisher表删除或更新相应的pubid记录
price售价double
state库存状态varchar(20)默认为'在库'
  • beanbooklendrecord

image-20220502161312696

字段名中文名称数据类型能否为空说明
idIDint主码,添加了AUTO_INCREMENT,每次插入新记录时会自动地创建主键字段的值
readerid读者IDvarchar(20)外码,拥有名为fk_reader_idx的普通索引,拥有名为fk_reader的约束,不允许beanreader表删除或更新相应的readerid记录
bookBarcode书的条码varchar(20)外码,拥有名为fk_book_idx的普通索引,拥有名为fk_book的约束,不允许beanbook表删除或更新相应的bookBarcode记录
lendDate借书日期datetime
returnDate归还日期datetime默认为空
lendOperUserid处理借出操作的用户IDvarchar(20)外码,拥有名为fk_lendOper_idx的普通索引,拥有名为fk_lendOper的约束,不允许beansystemuser表删除或更新相应的userid记录
returnOperUserid处理归还操作的用户IDvarchar(20)外码,默认为空,拥有名为fk_returnOper_idx的普通索引,拥有名为fk_returnOper的约束,不允许beansystemuser表删除或更新相应的userid记录
penalSum罚款double默认为0
  • beanpublisher

image-20220502161358675

字段名中文名称数据类型能否为空说明
pubid出版社IDvarchar(20)主码
publisherName出版社名varchar(50)拥有名为publisherName_UNIQUE 的唯一约束
address地址varchar(200)默认为空
  • beanreader

image-20220502161457559

字段名中文名称数据类型能否为空说明
readerid读者IDvarchar(20)主码
readerName读者名varchar(50)
readerTypeId读者类型IDint
lendBookLimitted借书限制int
createDate创建时间datetime
creatorUserId创建读者的用户的IDvarchar(20)外码,默认为空,拥有名为fk_stopper_idx的普通索引,拥有名为fk_stopper的约束,不允许beansystemuser表删除或更新相应的userid记录
removeDate删除时间datetime默认为空
removerUserId删除读者的用户的IDvarchar(20)外码,默认为空,拥有名为fk_remover_idx的普通索引,拥有名为fk_remover的约束,不允许beansystemuser表删除或更新相应的userid记录
stopDate封停时间datetime默认为空
stopUserId封停读者的用户的IDvarchar(20)外码,默认为空,拥有名为fk_stopper_idx的普通索引,拥有名为fk_stopper的约束,不允许beansystemuser表删除或更新相应的userid记录
  • beanreadertype

image-20220502161534080

字段名中文名称数据类型能否为空说明
readerTypeId读者类型IDint主码,添加了AUTO_INCREMENT,每次插入新记录时,会自动地创建主键字段的值
readerTypeName读者类型名varchar(50)拥有名为readerTypeName_UNIQUE的唯一约束
lendBookLimitted借书限制int
  • beansystemuser

image-20220502161557989

字段名中文名称数据类型能否为空说明
userid用户IDvarchar(20)主码
username用户名varchar(50)
pwd密码varchar(32)
usertype用户类型varchar(20)
createDate创建时间datetime
removeDate删除时间datetime默认为空

B、通过脚本默认加入的数据在哪张表?

beansystemuser

2、JDK、Eclipse安装和配置:参考讲义中的安装配置过程

3、在Eclipse中建立并运行图书管理系统工程:参考讲义中的过程

A、用管理员账号登陆后的界面

image-20220504110458109

image-20220504110214241

image-20220504110255905

image-20220504110328642

image-20220504110550481

image-20220504110608613

4、程序分析

A、分析用户管理模块,描述用户添加、重置密码、删除过程涉及的java类、数据库表,并说明实现该功能的流程(说明哪个类实现什么功能、数据库表发生什么变化)

用户添加

image-20220504111009114

  • 主要涉及Java类:
    • BaseException.java
    • DBUtil.java
    • BeanSystemUser.java
  • 涉及数据库:
    • BeanSystemUser
  • 流程
    • 首先调用了BeanSystemUser.java类创建了user用户对象来读取输入的创建用户的基本信息,判断输入的信息格式等是否正确,如果有误抛出BaseException.java类定义错误。正确之后调用DBUtil.java类来连接数据库,执行 sql 语句通过查询userid确认是否存在相同用户,确认无相同用户后执行 sql 语句向BeanSystemUser数据库插入数据创建用户

重置密码

image-20220504112535526

  • 主要涉及Java类:
    • BaseException.java
    • DBUtil.java
  • 涉及数据库:
    • BeanSystemUser
  • 流程
    • 首先调用了DBUtil.java类来连接数据库,执行 sql 语句通过查询userid确认是否存在用户,确认用户存在后执行sql语句更新BeanSystemUser数据库数据修改密码

删除用户

image-20220504112704363

  • 主要涉及Java类:
    • BaseException.java
    • DBUtil.java
  • 涉及数据库:
    • BeanSystemUser
  • 流程
    • 首先调用了DBUtil.java类来连接数据库,执行 sql 语句通过查询removeDate确认是否存在用户和用户是否已删除(如果存在removeDate字段且为空则为用户存在且未删除,如果存在字段不为空则说明已经删除,如果不存在字段则认为是不存在用户),确认用户存在并且未删除后后执行 sql 语句更新BeanSystemUser数据库数据删除用户

B、分析读者管理模块,描述读者查询过程涉及的java类、数据库表,并说明实现该功能的流程(说明哪个类实现什么功能)

精确查询

image-20220504154248092

  • 主要涉及Java类:
    • BeanReader.java
    • DBUtil.java
  • 涉及数据库:
    • BeanReader
    • BeanReaderType
  • 流程
    • 首先调用了DBUtil.java类来连接数据库,根据输入的readerid执行sql语句查询数据,查询到之后再新建BeanReader.java对象将值保存到新对象中,然后返回包含该对象的结果

模糊查询

image-20220504154714047

  • 主要涉及Java类:
    • BeanReader.java
    • DBUtil.java
  • 涉及数据库:
    • BeanReader
    • BeanReaderType
  • 流程
    • 首先调用了DBUtil.java类来连接数据库,如果输入里面的readerTypeId是大于0的整数,就在原查询语句上加入该条件;再判断keyword如果是非空,就把keyword也加入查询语句中。由于无法判断keywordreaderName还是readerid的关键字,所以使用or来添加条件,查询到之后再新建BeanReader.java对象将值保存到新对象中,然后返回包含该对象的结果

C、分析图书借阅模块,描述图书借阅流程(说明哪个类实现什么功能、数据库表发生什么变化,说明不能进行图书借阅的几种情况),说明java是如何进行事务管理的

image-20220504155557881

  • 主要涉及Java类:
    • BeanReader.java
    • ReaderManager.java
    • BookManager.java
    • DBUtil.java
    • BeanBook.java
  • 涉及数据库:
    • BeanReader
    • BeanReaderType
  • 流程
    • 首先调用了ReaderManager.java类根据输入的readerid查询了相应的BeanReader对象,在确认对象存在后根据BeanReaderRemoveDateStopDate判断读者是否注销或挂失。确认没有后再调用BookManager.java类根据输入的barcode查询对应的BeanBook对象,根据该对象的State属性判断是否在库,确认在库之后再调用loadReaderLentBooks来判断BeanReader对象的借书数量是否已达到限额,确认未达到限额后调用DBUtil.java连接数据库执行sql语句插入借书信息数据和更新书本的状态

5、利用Statement对象和Result对象实现按出版社名称精确查询出版社功能(精确查询是指查询的目标和查询条件中值完全相同的数据)。

  • 第一步:在cn.edu.zucc.booklib.control. PublisherManager类中添加按出版社名称精确查询方法 public BeanPublisher loadPubByName(String name)throws BaseException
  • 第二步:编写上述方法,要求当相应名字的出版社不存在时,返回null值;相关代码请参考提取所有出版社函数。
  • 第三步:启动booklib主程序,在出版社管理中录入几个出版社
  • 第四步:清空cn.edu.zucc.booklib.control.PublisherManager类中的main函数现有内容
  • 第五步:在main函数中编写代码,通过调用上面实现的方法按出版社名字查询出版社,如果返回null,则在控制台输出“没有找到出版社”,否则输出出版社编号。(注:控制台输出通过System.out.println(…)函数实现,函数调用的方法参考现有main函数中的内容)。要求main函数中调用两次上述函数,参数分别为一个确实存在的出版社,一个不存在的出版社。
  • 第六步:以java application模式运行PublisherManager类,查看输出内容。

A、请给出查询函数的代码。

public BeanPublisher loadPubByName(String name)throws BaseException {
        List<BeanPublisher> result = new ArrayList<BeanPublisher>();
        Connection conn = null;
        try {
            conn = DBUtil.getConnection();
            String sql = "select pubid,publisherName,address from beanpublisher where publisherName=?";
            sql +=  " order by pubid";
            java.sql.PreparedStatement pst = conn.prepareStatement(sql);
            pst.setString(1, name);
            java.sql.ResultSet rs = pst.executeQuery();
            if (rs.next()) {
                BeanPublisher r = new BeanPublisher();
                r.setPubid(rs.getString(1));
                r.setPublisherName(rs.getString(2));
                r.setAddress(rs.getString(3));
                return r;
            }
        } catch (SQLException e) {
            e.printStackTrace();
            throw new DbException(e);
        } finally {
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
        return null;
    }

image-20220504180430874

public static void main(String[] args) {
        PublisherManager p = new PublisherManager();
        try {
            BeanPublisher first = p.loadPubByName("老李出版社");
            BeanPublisher second = p.loadPubByName("小树出版社");

            if(first != null) {
                System.out.println(first.getPubid() + "," + first.getPublisherName() + "," + first.getAddress());
            } else {
                System.out.println("没有找到出版社");
            }

            if(second != null) {
                System.out.println(second.getPubid() + "," + second.getPublisherName() + "," + second.getAddress());
            } else {
                System.out.println("没有找到出版社");
            }
        } catch (BaseException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

image-20220504181400226

B、说明如何通过JDBC API判断没有查询到指定名字的出版社。

通过boolean java.sql.ResultSet.next() throws SQLException方法获取每次sql语句查询结果,每次调用next()就读入一行数据,当返回false就说明此时指针位于最后一行之后。如果查询后第一次调用next()就返回false,就可以判断没有查询到指定名字的出版社。

6、利用Statement对象和Result对象实现按出版社名称模糊查询出版社功能(模糊查询是指查询的目标包含输入的条件)。

  • 第一步:在cn.edu.zucc.booklib.control. PublisherManager类中添加按出版社名称精确查询方法 public List searchPubsByName(String name)throws BaseException
  • 第二步:编写上述方法,相关代码请参考提取所有出版社函数。
  • 第三步:清空cn.edu.zucc.booklib.control. PublisherManager类中的main函数现有内容
  • 第四步:在main函数中编写代码,通过调用上面实现的方法按出版社名字模糊查询出版社,并输出查询到的出版社信息。
  • 第六步:以java application模式运行PublisherManager类,查看输出内容。

A、请给出查询函数的代码。

public List<BeanPublisher> searchPubsByName(String name)throws BaseException {
        List<BeanPublisher> result = new ArrayList<BeanPublisher>();
        Connection conn = null;
        try {
            conn = DBUtil.getConnection();
            String sql = "select pubid,publisherName,address from beanpublisher";
            if (name != null && !"".equals(name)) {
                sql += " where publisherName like ?";
            }
            sql += " order by pubid";
            java.sql.PreparedStatement pst = conn.prepareStatement(sql);
            if (name != null && !"".equals(name)) {
                pst.setString(1, "%" + name + "%");
            }
            java.sql.ResultSet rs = pst.executeQuery();
            while (rs.next()) {
                BeanPublisher r = new BeanPublisher();
                r.setPubid(rs.getString(1));
                r.setPublisherName(rs.getString(2));
                r.setAddress(rs.getString(3));
                result.add(r);
            }
        } catch (SQLException e) {
            e.printStackTrace();
            throw new DbException(e);
        } finally {
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
        return result;
    }
public static void main(String[] args) {
        BeanPublisher p;
        PublisherManager pm = new PublisherManager();
        try {
            List<BeanPublisher> lst = pm.searchPubsByName("出版社");
            for (BeanPublisher beanPublisher : lst) {
                p = beanPublisher;
                System.out.println(p.getPubid() + "," + p.getPublisherName() + "," + p.getAddress());
            }
        } catch (BaseException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

image-20220504185841192

B、比较精确查询和模糊查询方法,说明在SQL语句中的主要区别。

主要区别在于where语句后的条件,精确查询使用的符号为=,只有完全等号两边完全相同才能返回true,从而实现精确查询;模糊查询使用的是LIKE谓词和%(代表任意长度),而"%name%"就代表可以匹配全部包含name子串的字符串,从而实现模糊查询。