基于java+swing+CS的图书销售管理系统GUI设计与实现(全套源码+配套论文)
大家好,今天给大家介绍基于java+swing+CS的图书销售管理系统GUI设计与实现,更多精选毕业设计项目实例见文末哦。
文章目录:
1、项目简介
- 基于C/S的图书销售管理系统是专为图书销售单位而设计,它取代了过去会计、出纳的“账本式”记录与管理,在给工作人员带来方便快捷的同时也大大提高了他们的工作效率,加快了管理步伐,避免了账目混乱,任务繁重等问题,符合现代办公的需求。
- 本系统主要使用Java中的GUI(图形用户界面)和AWT编程,开发环境为Eclipse,数据库采用MySQL5.0。整体框架采用三层架构模式,即显示层、业务层、数据层,大大降低了程序的耦合性。系统的主要功能包括:销售管理、库存管理、书本管理、种类管理、出版社管理五大功能模块,方便快捷地帮助书店管理人员实现了对图书进、存、销三方面的管理。
2、资源详情
项目难度:中等难度
适用场景:相关题目的毕业设计
配套论文字数:27474个字
包含内容:整套源码+完整毕业论文+答辩PPT+任务书
很多同学询问怎么下载开题报告、论文答辩、课题报告等ppt模板,给大家找了个好用的下载工具,可以试试:
3、关键词
图书;销售管理系统;C/S结构;java;swing;4、资源下载
本项目源码及论文如下,有需要的朋友可以点击进行下载。如果链接失效可点击下方卡片扫码自助下载。
序号 | 毕业设计全套资源(点击下载) |
---|---|
本项目源码 | 基于java+swing+CS的图书销售管理系统GUL设计与实现(源码+文档)_JAVA_swing_图书销售管理系统.zip |
5、毕设简介
提示:以下为毕业设计的简略介绍,项目源码及完整毕业论文下载地址见文末。
绪论
1.1 课题背景
中小型书店最繁重的工作就是图书进、存、销三方面的工作,因此只需要设计一款简单而实用的软件,帮助书店工作人员轻松实现这三方面的工作。混乱的界面,繁琐的操作不仅难以达到轻松管理的目的,还容易使工作人员情绪暴躁,影响书店效益。因此,我的目标是做一款界面清晰,操作方便的简单管理软件。
省略
1.2 目的和意义
图书销售管理系统开发的目的是为图书销售单位提供图书信息管理和提高图书销售管理工作的效率。
省略
1.3 开发工具及技术
本系统基于C/S结构设计,采用三层架构体系,编写系统主要用到Java中的GUI(图形用户界面)和AWT编程,开发环境为Eclipse,数据库采用MySQL5.0并以Navicat for MySQL作为辅助工具。
下面分别对C/S模型、三层架构模式、GUI、AWT编程、Eclipse、MySQL 5.0数据库和Navicat for MySQL进行简要介绍。
1.3.1 开发工具
(1) Eclipse
Eclipse是著名的跨平台的自由集成开发环境(IDE)。最初主要用来Java语言开发,通过安装不同的插件Eclipse可以支持不同的计算机语言,比如C++和Python等开发工具。Eclipse的本身只是一个框架平台,但是众多插件的支持使得Eclipse拥有其他功能相对固定的IDE软件很难具有的灵活性。许多软件开发商以Eclipse为框架开发自己的IDE.
省略
(2) MySQL 5.0
MySQL 5.0是一个关系型数据库管理系统,由瑞典MySQL AB公司开发,目前属于Oracle公司。Mysql是最流行的关系型数据库管理系统,在WEB应用方面MySQL是最好的RDBMS(Relational Database Management System:关系数据库管理系统)应用软件之一。
省略
(3) Navicat for MySQL
Navicat for MySQL是一款强大的 MySQL 数据库管理和开发工具,它为专业开发者提供了一套强大的足够尖端的工具,但对于新用户仍然易于学习。Navicat for MySQL 基于Windows平台,为 MySQL 量身订作,提供类似于 MySQL 的用管理界面工具。此解决方案的出现,将解放 PHP、J2EE 等程序员以及数据库设计者、管理者的大脑,降低开发成本,为用户带来更高的开发效率。
省略
1.3.2 开发语言
主要使用Java语言中的GUI(图形用户界面)和AWT(抽象窗口工具包)编程。
(1) GUI
图形用户界面(Graphical User Interface,简称 GUI,又称图形用户接口),即人机交互图形化用户界面设计,是指采用图形方式显示的计算机操作用户界面。与早期计算机使用的命令行界面相比,图形界面对于用户来说在视觉上更易于接受。
(2) AWT
抽象窗口工具包(Abstract Windowing Toolkit,缩写为AWT)是Java的平台独立的窗口系统图形和用户界面器件工具包。AWT 是Java基础类 (JFC)的一部分,为Java程序提供图形用户界面(GUI)的标准API。 AWT提供了JavaApplet和Java Application中可用的用户图形界面GUI中的基本组件(components)。由于Java是一种独立于平台的程序设计语言,但GUI却往往是依赖于特定平台的,Java采用了相应的技术使得AWT能提供给应用程序独立于机器平台的接口,这保证了同一程序的GUI在不同机器上运行具有类似的外观。
1.3.3 开发结构及模式
(1) C/S结构
C/S 结构,即大家熟知的客户机和服务器结构。它是软件系统体系结构,通过它可以充分利用两端硬件环境的优势,将任务合理分配到Client端和Server端来实现,降低了系统的通讯开销。
省略
(2) 三层架构模式
三层架构模式即显示层、业务逻辑层、数据层。其工作模式:显示层往往面向操作者,主要用来接受数据、传递数据和显示数据;业务逻辑层主要是用来处理显示层接收到的数据和数据库中的数据;数据层主要用来对数据库进行操作,其中包括常见的增、删、改、查等操作。采用三层架构模式可以大大降低了程序的耦合性,符合现代软件的标准。
省略
1.4 软硬件需求
1.4.1 硬件需求
电脑:笔记本电脑或台式电脑
CPU:Pentium以上
内存: 512M以上
1.4.2 软件需求
操作系统版本:Windows XP /vista/Win7
开发工具:Eclipse
开发语言:Java
2 需求分析
2.1 可行性分析
可行性研究主要内容是要求以全面、系统的分析为主要方法,经济效益为核心,围绕影响项目的各种因素,运用大量的数据资料论证拟建项目是否可行。对整个可行性研究提出综合分析评价,指出优缺点和建议以确定该项目是否可行。
2.1.1 技术可行性
本系统开发工具是Eclipse和MySQL数据库,开发语言是Java,java是一种面向对象编程语言,简单易学而且灵活方便。数据库的设计和操作是本系统设计的核心,但在Navicat for MySQL的帮助下能够轻松便捷的对数据库进行操作。此外,大学期间学习过数据库操作语言,软件测试,UML统一建模语言等课程使系统分析、设计和测试不再成为难题。因此,完成系统实现在技术上完全具有可行性。
2.1.2 经济可行性
省略
2.1.3 操作可行性
该系统是本着界面简洁、操作方便的目的进行开发,主要目的是帮助图书销售商对图书进行进、存、销三方面管理。因此该系统在操作上具有功能简单,操作简洁的优势,完全可行。
2.1.4 法律可行性
省略
2.2 系统流程图
图2.1 系统流程图
如图2.1所示,由于系统是C/S模式,所以无需注册等操作,只需要在编程时将用户名和密码设定,如客户对用户名和密码有特殊要求,由编程人员将客户要求的用户名和密码事先输入程序当中,如本系统的用户名和密码都为yujian,该数据已经在数据库的T_USER表事先存好。如果客户输入错误,则要重新回到登录页面输入正确的账号密码。如果输入正确,则可以进入用户操作页面进行相应的操作。在进一系列的操作之后然后点击关闭按钮,即可退出系统。
2.3 系统用户用例图
系统只涉及图书的进、存、销三方面的功能,其所对应的功能模块分别是图书管理模块、库存管理模块、销售管理模块。此外,为方便用户在这三方面的操作更加简洁增加了出版社管理模块,种类管理模块,这样做一方面能够使用户界面简单易用,另一方面能够使用户在主要使用的三个重要界面有更多的选择而不是去输入,减少了错误发生的概率。
管理员即能够正确输入用户名和密码的操作人员,且其享有全部权限。因此管理员用例图十分清晰明了,如图2.2。
图2.2 管理员用例图
图2.2清晰明了的告诉我们用户登录是前提,只有用户经过正确的身份验证才可以操作本系统几个最重要的操作页面。
2.4 功能模块需求分析
本系统本着界面简洁大方,操作简单的原则实现以下功能:
1.系统界面清晰分明、简单易用,管理人员能够在短时间内熟练操作。
2.管理员享有销售管理、库存管理、图书管理、出版社管理、种类管理五大权限。
3.尽可能的让用户去选择,而不是输入,减少错误发生的概率。
4.增加主要功能的快捷键,增加操作的简易性。
系统的功能结构图,如图2.3。
图2.3 系统功能模块图
从图2.3中可以看到,系统分为六个模块,分别是管理人员登录模块,销售管理模块,入库管理模块,图书管理模块,种类管理模块和出版社管理模块,其中登录模块并不与其它模块相平行,登录模块只是决定操作人员是否有权限去操作其它五大模块。
2.5 设计的基本思路
设计思路遵循以下几点:
- 考虑到图书销售单位只是简单的对图书进行管理、记录,因此软件采用C/S结构,C/S结构的优点是客户端响应速度快,能充分发挥客户端PC的处理能力,很多工作可以在客户端处理后再提交给服务器。
- 采用三层架构模式。一方面能够使开发人员的逻辑思维清晰,提高软件开发效率,另一方面降低了程序的耦合性,例如本程序如果要开发成为B/S结构只需要改动业务逻辑层即可。
- 模块化设计。模块化设计要求将整个系统划分成基于小的模块,有利于代码的重载,简化设计和实现过程。
- 效率第一。设计该系统的目的是让大量工作人员从繁琐的工作中解脱出来,因此本系统必须达到提高工作效率的目的。
- 界面清晰明了,操作简单。方便用户使用,让用户能够在最短时间轻松方便的适应系统的操作。
- 系统设计不仅要功能完善还要突出重点。要符合设计需求,在有可能改进的地方进行扩充,使系统更适应用户的需要。
2.6 性能需求
2.6.1 系统的易操作性
为使工作人员能够轻松操作该系统,需做到以下要求:
1.操作界面不涉及跳转,要操作的界面覆盖已显示的界面。
2.主要功能界面尽可能的让用户去选择,而不是输入。
3.部分系统设计加入自动计算功能,且不可更改。
4.在操作发生错误时出现友好提示。
2.6.2 数据的正确性和完整性
1.数据库设计过程中明确哪些数据可以为空,哪些数据不能为空。
2.关系表中的相关数据必须明确统一。
3.具有自动判断功能,如销售数量大于库存数量时应给出友好的错误提示。
4.具有自动加、减、乘功能,保证单价、总量等数据的正确性。
5.交易日期根据系统时间而显示、记录,且不可更改。
2.6.3 数据的安全性
只有被授权的管理人员,即知道账户和密码的人才可进入系统。
2.7 界面需求
界面设计是整个设计很重要的一部分,好的界面不仅方便管理人员操作,还可以使操作人员有一个阳光的心情,提高工作人员工作效率,因此界面的设计必须满足以下几点要求:
1.简易性
界面的简洁是让用户便于了解,便于使用,并能减少用户发生错误选择的可能性。
2.用户语言
界面中要使用能反应用户本身的语言,而不是程序员的语言。
3.记忆负担最小化
人脑不是电脑,在设计界面时必须要考虑大脑处理信息的限度。人类的短期记忆极不稳定,且有限,24小时内存在25%的遗忘率。所以对用户来说,浏览信息要比记忆更加重要。
4.一致性
一致性是每个优秀界面都具备的特点。界面的结构必须清晰且一致,与客户的需求一致。
5.清楚
视觉上便于理解与使用。
6.用户熟悉程度
用户可通过已掌握的知识来使用界面,但不应超出一般常识。
7.人性化
高效率和用户满意度是人性化的体现。想用户所想,做用户所做,用户总是按照他们自己的方法理解和使用。
8.安全性
用户做出危险的选择使有信息介入系统的提示。
3 系统分析与设计
3.1 数据库的分析与设计
数据库是整个系统的各个部分能否紧密地结合在一起以及如何结合的关键所在,因此数据库是系统的核心和基础。的计算机信息系统以数据库为核心,在数据库管理系统的支持下,进行信息的收集、整理、存储、检索、更新、加工、统计和传播等操作。数据库设计的好坏直接影响到整个系统的质量和效率。
数据库的设计一般经过需求分析、概念设计、逻辑设计、物理设计4个步骤。
3.1.1 数据库的需求分析
首先可以确定需要一个用户表,其中储存的数据有用户名和密码,以作验证之用。其次,系统相关的表,从最基础开始,有出版社表、书的类型表、书表、入库记录表和销售记录表,其中,一条入库记录中涉及多本书,一条销售记录也涉及多本书,因此还需要书的入库记录表和书的销售记录表,一条书的入库记录对应一本书,该条书的入库记录属于某一条入库记录,可以理解成这是书与入库记录的关系表,同样地,书的销售记录与书的入库记录一样。
3.1.2 数据库的概念结构设计
省略
数据库的概念结构设计采用实体—联系(E-R)模型设计方法。E-R模型用E-R图表示,E-R模型法的组成元素有:实体、属性、联系。
本系统概念设计图有:
图3.1 管理员实体E-R图
图3.1所示为管理员信息的E-R图,其中包括的信息有ID、用户名和密码,ID是隐藏列,作为主键。用户名和密码作为是否有权操作系统的验证信息储存在数据库当中。
图3.2 出版社实体E-R图
图3.2是出版社信息的E-R图,主要包括出版社名称、联系人、联系电话、简介四方面,存储这些信息的主要目的是方便图书管理人员联系出版社以进行进退货的协商。
图3.3 书本信息实体E-R图
图3.3为书本信息的E-R图,主要包括的信息有书名、作者、单价、缩略图和库存数量。书名、单价和作者是由操作人员输入并储存在数据库当中的。库存数量是当前书本的数目,当数目发生改变时,数据会自动更新。
图3.4 种类信息实体E-R图
图3.4是种类信息的E-R图,主要包括种类名称、简介两部分信息。由于种类管理页面是作为图书管理页面的辅助页面设计的,其数据库中的信息也是为图书管理信息设计的。
图3.5 入库信息实体E-R图
图3.5是入库信息的E-R图,因为库存管理页面有个按日期查询入库信息的功能,为此,需要设计一个记录入库日期的数据库。
图3.6 交易信息实体E-R图
图3.6是交易信息的E-R图,设计这样一个数据的目的同设计入库信息的目的是一样的,都是为了方面管理人员进行查询。
图3.7 书的入库信息实体E-R图
如图3.7,书的入库信息不同于入库信息,书的入库信息包括入库书本的名称、入库数量和相关的入库记录三方面的信息。入库管理信息页面中按日期查询入库记录所查询到的内容正是此数据中记录的相关信息。
图3.8 销售信息实体E-R图
图3.8是销售信息的E-R图,与图3.7相似,这个数据库的设计主要是保存销售的书本、数量和当天的销售记录信息,其主要目的一方面是记录,另一方面是方便管路人员查询。
3.1.3 数据库的逻辑结构设计
由于数据库概念模型独立于任何特定的数据库管理系统,因此,需要根据具体使用的数据库管理系统的特点进行转换。即转化为按计算机观点处理的逻辑关系模型,E-R模型向关系数据库模型转换应遵循下列原则:
1.每一个实体要转换成一个关系
2.所有的主键必须定义非空(NOT NULL)
3.对于二元联系应按照一对多、弱对实、一对一、多对多等联系来定义外键。
根据E-R模型,图书管理系统建立了以下逻辑数据结构,下面是各数据表的详细说明:
1.管理员信息表只记录账户与密码,以便登录用。表结构如表3.1所示。
表3.1 管理员信息表(t_user)
2.种类信息表主要是记录了书的种类的基本信息,表结构如表3.2所示。
表3.2 书的种类信息表(t_book_type)
3.出版社信息表主要是记录了出版社的相关信息,表结构如图3.3所示。
表3.3 出版社信息表(t_publisher)
4.书本信息表主要记录了与书相关的所有内容,表结构如表3.4示。
表3.4 书本信息表(t_book)
5.交易记录表, 一个交易记录包括多个书的销售记录, 一次交易可能有多本书,表结构如表3.5示。
表3.5 书本交易信息表(t_sale_record)
6.书的销售记录, 一条记录对应一本书,表结构如表3.6示。
表3.6 书本信息表(t_book_sale_record)
7.入库记录表, 一次入库会入多本书,表结构如表3.7所示。
表3.7 入库记录表表(t_book_sale_record)
8.书的入库记录表, 其中包括入库的书、对应的入库记录、入库数量等信息,表结构如表3.8所示。
表3.8 入库记录表表(t_book_in_record)
3.2 连接数据库的前期准备工作
数据库中的表已经建好,在系统功能实现时只需要建立每个表相对应的类即可,要使客户对书本的管理信息存入数据库就必须将数据库与程序联系起来。为此,需要做一些工作将数据库“嵌入”系统之中。
3.2.1 编写配置读取类
连接数据库就要先做数据库相关的一些配置,例如对应数据库的相关驱动、数据库地址、用户名和密码,然后可以放到配置文件中,如果需要更换数据库或者地址,只需要修改这份配置文件即可。
建立配置文件jdbc.properties,内容如下:
//JDBC驱动
jdbc.driver=com.mysql.jdbc.Driver
//连接地址
jdbc.url=jdbc:mysql://localhost:3306/book_system
//数据库用户名
jdbc.user=yujian
//密码
jdbc.pass=yujian
建立好该文件后,再编写类去读取该文件,获得所需要的值即可。用于读取配置的PropertiesUtil类,代码如下:
public class PropertiesUtil {
//该记录所对应的书的销售数量
private static Properties properties = new Properties();
private static String CONFIG = "/cfg/jdbc.properties";//配置文件的路径
//读取资源文件, 设置输入流
private staitc InputStream is =propertiesUtil.class.
getResourceAsStream(CONFIG);
public static String JDBC_DRIVER; //数据库驱动
public static String JDBC_URL; //jdbc连接url
public static String JDBC_USER; //数据库用户名
public static String JDBC_PASS; //数据库密码
static {
properties.load(is); //加载输入流
//获得配置的各个属性
JDBC_DRIVER = properties.getProperty("jdbc.driver");
JDBC_URL = properties.getProperty("jdbc.url");
JDBC_USER = properties.getProperty("jdbc.user");
JDBC_PASS = properties.getProperty("jdbc.pass");
}
}
public class PropertiesUtil {
//该记录所对应的书的销售数量
private static Properties properties = new Properties();
private static String CONFIG = "/cfg/jdbc.properties";//配置文件的路径
//读取资源文件, 设置输入流
private staitc InputStream is =propertiesUtil.class.
getResourceAsStream(CONFIG);
public static String JDBC_DRIVER; //数据库驱动
public static String JDBC_URL; //jdbc连接url
public static String JDBC_USER; //数据库用户名
public static String JDBC_PASS; //数据库密码
static {
properties.load(is); //加载输入流
//获得配置的各个属性
JDBC_DRIVER = properties.getProperty("jdbc.driver");
JDBC_URL = properties.getProperty("jdbc.url");
JDBC_USER = properties.getProperty("jdbc.user");
JDBC_PASS = properties.getProperty("jdbc.pass");
}
}
3.2.2 编写JDBC操作类
JDBC是Java Data Base Connectivity的简称,是Java中进行数据库连接的技术。JDBC的API提供了标准统一的SQL数据存取接口,可以让程序员不需要关心如何去连接不同的数据库,只需为不同的数据库提供不同的驱动,就可以达到连接不同数据库的要求。
配置好了之后,开始着手编写数据库的操作类,首先肯定是帮我们进行数据库连接,我们之前配置了连接的相关属性,接着我们需要这个类帮我们提供查询、执行SQL等功能。确定好目标后,开始编写。
新建JDBCExecutor类,该类具有属性如下:
private static String DRIVER = PropertiesUtil.JDBC_DRIVER; //获得驱动
private static String URL = PropertiesUtil.JDBC_URL; //获得url
//获得连接数据库的用户名
private static String USER = PropertiesUtil.JDBC_USER;
//获得连接数据库的密码
private static String PASS = PropertiesUtil.JDBC_PASS;
private Connection connection; //连接对象
private static JDBCExecutor jdbcExecutor; //维护一个本类型的对象
private Statement stmt; //维护一个本类型的对象
private static String DRIVER = PropertiesUtil.JDBC_DRIVER; //获得驱动
private static String URL = PropertiesUtil.JDBC_URL; //获得url
//获得连接数据库的用户名
private static String USER = PropertiesUtil.JDBC_USER;
//获得连接数据库的密码
private Connection connection; //连接对象
private static String PASS = PropertiesUtil.JDBC_PASS;
private static JDBCExecutor jdbcExecutor; //维护一个本类型的对象
private Statement stmt; //维护一个本类型的对象
注:由于创建一个Connection对象需要耗费很大的资源,因此使用单态模式,让JDBCExecutor类维护一个JDBCExecutor对象,可以在构造器中创建Connection,由于JDBCExecutor是单态的,因此可以保证在应用中只创建一个Connection。
然后在JDBCExecutor的构造器中创建各个对象,再提供一个方法返回JDBCExecutor的实例。
private JDBCExecutor() {
//初始化JDBC驱动并让驱动加载到jvm中
Class.forName(DRIVER);
//创建数据库连接
connection = DriverManager.getConnection(URL, USER, PASS);
//创建Statement对象
stmt = connection.createStatement();
}
public static JDBCExecutor getJDBCExecutor() {
//如果本类所维护jdbcExecutor属性为空,则调用私有的构造器获得实例
if (jdbcExecutor == null) jdbcExecutor = new JDBCExecutor();
return jdbcExecutor;
}
private JDBCExecutor() {
//初始化JDBC驱动并让驱动加载到jvm中
Class.forName(DRIVER);
//创建数据库连接
connection = DriverManager.getConnection(URL, USER, PASS);
//创建Statement对象
stmt = connection.createStatement();
}
public static JDBCExecutor getJDBCExecutor() {
//如果本类所维护jdbcExecutor属性为空,则调用私有的构造器获得实例
if (jdbcExecutor == null) jdbcExecutor = new JDBCExecutor();
return jdbcExecutor;
}
最后只需要编写一些简单查询功能,并把查询后的结果保存在一个对象中即可:
public ResultSet executeQuery(String sql) {
//利用Statement对象执行参数的sql
ResultSet result = stmt.executeQuery(sql);
return result;
}
public int executeUpdate(String sql) {
int result = -1;
//执行SQL语句
stmt.executeUpdate(sql);
//获得主键
ResultSet rs = stmt.getGeneratedKeys();
while(rs.next()) result = rs.getInt(1); //返回最后一个主键
rs.close();
return result;
}
3.2.3 创建数据转换工具类
DBCExecutor中提供了一个executeQuery方法,该方法返回ResultSet对象,需要对该结果集进行一些封装。因此需要创建一个工具类,对该结果集进行封装,并返回对应的集合。具体的代码如下。
public static Collection getDatas(Collection result, ResultSet rs, Class clazz) {
while (rs.next()) {
//创建类的实例
Object vo = clazz.newInstance();
//获取本对象的属性
Field[] fields = clazz.getDeclaredFields();
Field[] allFields = addFields(superFields, fields);
//遍历所有的属性
for (Field field : allFields) {
//获得setter方法的方法名
String setterMethodName = getSetterMethodName(field.getName());
//获得setter方法
Method setterMethodName =
clazz.getMethod(setterMethodName, field.getType());
invokeMethod(rs, field, vo, setterMethod);
}
result.add(vo);
}
rs.close();
return result;
}
private static void invokeMethod(ResultSet rs, Field field, Object vo,
Method setterMethod) {
String value = rs.getString(field.getName());
//从ResultSet中获取与该对象属性名一致的字段, 并执行setter方法
setterMethod.invoke(vo, value);
}
private static String getSetterMethodName(String fieldName) {
String begin = fieldName.substring(0, 1).toUpperCase();
String end = fieldName.substring(1, fieldName.length());
String methodName = "set" + begin + end;
return methodName;
}
private static Field[] addFields(Field[] f1, Field[] f2) {
List<Field> l = new ArrayList<Field>();
for (Field f : f1) l.add(f);
for (Field f : f2) l.add(f);
return l.toArray(new Field[f1.length + f2.length]);
}
至此,已经完成了前期所有准备工作,接下来就可以按照客户的需求,得到的数据以及之前的设计来实现系统的功能。
4 系统功能的实现
数据库设计完成后,就开始进入具体的应用程序的分析与设计阶段。应用程序的分析与设计主要包括了应用程序各个功能模块的总体规划和分析、应用程序界面的选择与设计以及应用程序结构的选择等部分,而系统开发工作主要是集中在逻辑、功能和技术设计上,系统实施阶段要继承此前面各个阶段的工作成果,将技术设计转化为物理实现,因此系统实施的成果是系统分析和设计阶段的结晶。
4.1 创建数据库对象
已经设计了系统所需要的各个表,包括出版社表、书种类表、书表等,接着需要为这些表建立相应的对象,每一个表可以对应一个对象。
首先新建出版社所对应的实体Concern类,一个Concern对象代表一个出版社,该类继承于ValueObject,此外,出版社表中有的字段,都需要在这个类中反应出来。
Concern类:
public class Concern extends ValueObject {
private String PUB_NAME;//出版社名称
private String PUB_TEL; //出版社电话
private String PUB_LINK_MAN; //联系人
private String PUB_INTRO; //简介
}
Type类:
public class Type extends ValueObject {
private String TYPE_NAME; //名称
private String TYPE_INTRO; //简介
}
User类:
public class User extends ValueObject {
private String USER_NAME; //对应书的外键, 从数据库查出来时有值
private String USER_PASSWORD; //对应销售记录外键
}
Book类:
public class Book extends ValueObject {
private String BOOK_NAME; //书本名称
private String BOOK_INTRO; //简介
private String BOOK_PRICE; //书的单价
private String TYPE_ID_FK; //种类外键
private String PUB_ID_FK; //出版社外键
private String REPERTORY_SIZE; //存储量
private String IMAGE_URL; //图片url
}
InRecord类:
public class InRecord extends ValueObject {
private String RECORD_DATE; //入库日期
}
BookInRecord类:
public class BookInRecord extends ValueObject {
private String BOOK_ID_FK; //对应书的外键, 从数据库查出来时有值
private String T_IN_RECORD_ID_FK; //对应销售记录外键
private String IN_SUM; //入库数量
}
SaleRecord类:
public class SaleRecord extends ValueObject {
private String RECORD_DATE; //交易日期
}
BookSaleRecord类:
public class BookSaleRecord extends ValueObject {
private String BOOK_ID_FK; //该记录对应的书的外键
private String T_SALE_RECORD_ID_FK; //该记录对应的销售记录的外键
private String TRADE_SUM; //该记录所对应的书的销售数量
}
4.2 管理员登陆页面
1.描述:为了保证系统的安全性,要使用本系统必须先登陆到系统中。
2.程序运行效果图如图4.1所示。
图4.1 管理员登陆页面设计
3.在登陆页面输入用户名和密码以,点击登录按钮,跳转到登录的service中,在该service中会对用户名,密码进行判断,如果正确则进入到管理界面,如果错误则提示“用户名或密码不正确”,页面重新调转到登陆页。
首先编写用户业务接口:
关键代码:
public interface extends UserService {
/**
* 用户登录的方法,如果登录失败,则抛出BusinessException
*/
Void login(String name,String password)
}
BusinessException异常实现代码:
public class BusinessException extends RuntimeException {
public BusinessException(String m) {
super(m);
}
}
实现用户业务层接口关键代码:
public class UserServiceImpl implements UserService {
private UserDao userDao;
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}
public void login(String name, String password) {
User user = userDao.getUser(name, password);
if (user == null) {
throw new BusinessException("用户名或密码错误");
}
}
}
在完成登录界面以后我们就可以继续编写其他主要功能界面,在编写开始之前我们要将系统分为三层:表现层、业务层、数据访问层,这样分层的好处在于,如果视图层发生变化,例如不再使用swing作为表现层,使用jsp的话,那么,业务层、数据访问层的代码将不需要改变,达到重用的目的。业务层与数据访问层分别提供各自的接口,在表现层中使用业务层的接口,业务层中使用数据访问层的接口,就算实现发生了改变,也可以不用去更改调用者的代码,当需要更改某一部分实现的时候,直接更换实现类即可。
4.3 设计父类
由于每个表中都有ID一列因此可以为每个表的对象先建立一个父类,提供ID字段。
父类主要代码:
public class ValueObject {
private String ID;
}
除了管理员登录界面,其它各个界面几乎都大同小异,界面上面部分是一个列表,下面是一个表单,因此可以把几个界面的共同部分提取出来作为每个界面对象(JPanel)的父类,将一些可以重用的代码提升至父类。这样做不仅可以使我们的逻辑思维更加清晰,还可以减少代码量,提高开发效率。
首先,在数据访问层建立一个父类,给该层各个DAO实现类去继续,提供一些可能每个DAO都会使用到的方法:
public class CommonDaoImpl {
//返回JDBCExecutor对象
public JDBCExecutor getJDBCExecutor() {
return JDBCExecutor.getJDBCExecutor();
}
//根据参数的SQL, 存放结果的集合对象, 和具体的数据库映射对象返回一个集合
public Collection getDatas(String sql, Collection<ValueObject> result,
Class clazz) {
//执行SQL返回ResultSet对象
ResultSet rs = getJDBCExecutor().executeQuery(sql);
//对ResultSet进行封装并返回集合
return DataUtil.getDatas(result, rs, clazz);
}
}
其次,创建各个界面对象的父类并提供一些抽象方法让子类去实现:
private JTable table; //存放数据的table
protected Vector<Vector> datas; //列表数据
public void setJTable(JTable table) {
this.table = table;
}
public JTable getJTable() {
return this.table;
}
public Vector<Vector> getDatas() {
return datas;
}
public void setDatas(Vector<Vector> datas) {
this.datas = datas;
}
//将数据设置进JTable中
public void initData() {
if (this.table == null) return;
DefaultTableModel tableModel = (DefaultTableModel)this.table.getModel();
//将数据设入表格Model中
tableModel.setDataVector(getDatas(), getColumns());
//设置表格样式
setTableFace();
}
//刷新列表的方法
public void refreshTable() {
initData();
getJTable().repaint();
}
//获取表列集合, 由子类去实现
public abstract Vector<String> getColumns();
//设置列表的样式, 由子类去实现
public abstract void setTableFace();
//设置数据列表的方法,由子类去实现
public abstract void setViewDatas();
//清空界面下边的列表
public abstract void clear();
//给子类使用的方法, 用于获取一个列表的id列值
public String getSelectId(JTable table) {
int row = table.getSelectedRow();
int column = table.getColumn("id").getModelIndex();
String id = (String)table.getValueAt(row, column);
return id;
}
4.4 出版社管理模块
出版社管理模块主要功能是记录图书销售单位供货商,即出版社的相关信息,其中有出版社名称,联系人,联系电话,出版社简介四大信息。该模块一方面能够帮助管理人员记录为本单位供货的出版社的信息,另一方面可以在图书管理模块作为被调用对象来显示。
程序运行效果图如图4.2所示。
图4.2 出版社主页面
分步骤实现以下功能
1.获取全部出版社功能
数据访问层接口关键代码如下:
public interface ConcernDao {
Collection<Concern> findAll();
}
实现接口方法的类关键代码如下:
public class ConcernDaoImpl extends CommonDaoImpl implements ConcernDao {
public Collection<Concern> findAll() {
String sql = "SELECT * FROM T_PUBLISHER pub ORDER BY pub.ID DESC";
return getDatas(sql, new Vector(), Concern.class);
}
}
业务层接口代码如下关键代码如下:
public interface ConcernService {
Collection<Concern> getAll();
}
实现业务层接口方法的类关键代码如下:
public class ConcernServiceImpl implements ConcernService {
//数据访问对象
private ConcernDao dao;
//在构造器中设置ConcernDao
public ConcernServiceImpl(ConcernDao dao) {
this.dao = dao;
}
//直接返回数据
public Collection<Concern> getAll() {
return dao.findAll();
}
}
实现表现层的view方法,得到出版社对象后,再将出版社的各个值设置到对应的地方,关键代码如下:
private Vector<Vector> changeDatas(Vector<Concern> datas) {
Vector<Vector> view = new Vector<Vector>();
for (Concern c : datas) {
Vector v = new Vector();
v.add(c.getID());
v.add(c.getPUB_NAME());
v.add(c.getPUB_LINK_MAN());
v.add(c.getPUB_TEL());
v.add(c.getPUB_INTRO());
view.add(v);
}
return view;
}
public void setViewDatas() {
Vector<Concern> concerns = (Vector<Concern>)service.getAll(); Vector<Vector> datas = changeDatas(concerns);
setDatas(datas);
}
2.模糊查询功能
编写实现数据层接口方法的类代码如下:
public Collection<Concern> findByName(String name) {
String sql = "SELECT * FROM T_PUBLISHER pub WHERE pub.PUB_NAME like '%" +name + "%' ORDER BY pub.ID DESC";
//调用父类方法返回结果集
return getDatas(sql, new Vector(), Concern.class);
编写实现数据层接口方法的类关键代码如下:
//实现ConcernService接口的方法
public Collection<Concern> query(String name) {
return dao.findByName(name);
}
实现显示层关键代码:
//按名字模糊查询
private void query() {
String name = this.queryName.getText();
Vector<Concern> concerns = (Vector<Concern>)service.query(name);
Vector<Vector> datas = changeDatas(concerns); //转换数据格式
setDatas(datas); //设置数据
refreshTable();//刷新列表
}
3.查看出版社功能
编写实现数据层接口方法的类关键代码如下:
//实现ConcernDao接口方法
public Concern find(String id) {
//查询的SQL
String sql = "SELECT * FROM T_PUBLISHER pub WHERE pub.ID = '" + id + "'";
//获得数据,并返回第一条
List<Concern> datas = (List<Concern>)getDatas(sql, new ArrayList(), Concern.class);
return (datas.size() == 1) ? datas.get(0) : null;
}
编写实现数据层接口方法的类关键代码如下:
//实现接口方法
public Concern find(String id) {
return dao.find(id);
}
实现显示层关键代码:
private void view(String id) {
Concern c = service.find(id); //通过业务接口得到出版社对象
this.concernId.setText(c.getID());//设置ID的JTextFiled(隐藏)
this.concernName.setText(c.getPUB_NAME());//设置出版社名称的文本框
this.pubLinkMan.setText(c.getPUB_LINK_MAN());//设置联系人的文本框
this.pubTel.setText(c.getPUB_TEL());//设置联系电话的文本框
this.pubIntro.setText(c.getPUB_INTRO());//设置简介的文本域
}
4.新增出版社功能
保存方法关键代码:
private void save() {
if (this.concernName.getText().trim().equals("")) {
//调用父类的方法,弹出错误提示
showWarn("请输入出版社名称");
return;
}
//如果id为空, 则为新增, 不为空则为修改操作
if (this.concernId.getText().equals("")) add();
else update();
}
//新增方法
private void add() {
//暂时不实现
}
//修改方法
private void update() {
//暂时不实现
}
为保存按钮添加监听器:
//保存按钮监听器
saveButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent arg0) {
save();
}
};
给ConcernDao添加新的接口方法:
//添加一个出版社并返回新增的id
String add(Concern concern);
实现add方法:
public String add(Concern c) {
//利用出版社对象拼装SQL
String sql = "INSERT INTO T_PUBLISHER VALUES (ID, '" + c.getPUB_NAME()+
c.getPUB_NAME() + "', '" + c.getPUB_TEL() + "', '" + c.getPUB_LINK_MAN() + "', '" + c.getPUB_INTRO() + "')";
//调用JDBCExecutor的executeUpdate方法,并返回新数据的主键
String id = String.valueOf(getJDBCExecutor().executeUpdate(sql));
return id;
}
实现业务逻辑层关键代码:
public Concern add(Concern c) {
String id = dao.add(c); //得到返回的ID
//根据ID再查找对象
return find(id);
}
显示层关键代码:
private Concern getConcern() {
String concernName = this.concernName.getText();
String pubTel = this.pubTel.getText();
String pubLinkMan = this.pubLinkMan.getText();
String pubIntro = this.pubIntro.getText();
return new Concern(null, concernName, pubTel, pubLinkMan, pubIntro);
}
//新增方法
private void add() {
Concern c = getConcern();//获得界面中的值并封装成对象
service.add(c);
setViewDatas();//重新读取数据
clear();//刷新表单
}
//实现父类的方法,清空表单并刷新列表
public void clear() {
refreshTable();//调用父类的刷新列表方法
//清空表单中的各个文本框(域)的值
this.concernId.setText("");
this.concernName.setText("");
this.pubLinkMan.setText("");
this.pubTel.setText("");
this.pubIntro.setText("");
}
5.修改出版社功能
数据层关键代码:
public String update(Concern c) {
//利用参数拼装修改的SQL
String sql = "UPDATE T_PUBLISHER pub SET pub.PUB_NAME='" + c.getPUB_NAME() + "', pub.PUB_TEL='" + c.getPUB_TEL() + "', pub.PUB_LINK_MAN='" + c.getPUB_LINK_MAN() +"', pub.PUB_INTRO='" + c.getPUB_INTRO() + "' WHEREpub.ID='" + c.getID() + "'";
//执行SQL
getJDBCExecutor().executeUpdate(sql);
//直接返回对象的ID
return c.getID();
}
业务逻辑层关键代码:
public Concern update(Concern c) {
//调用DAO方法修改对象
String id = dao.update(c);
//重新查找该对象
return find(id);
}
显示层关键代码:
private void update() {
//取得当前修改记录的ID
String id = this.concernId.getText();
//根据界面数据获得Concern对象
Concern c = getConcern();
c.setID(id);
service.update(c);
//重新读取数据
setViewDatas();
//刷新列表
refreshTable();
}
从上面的代码中可以看出,出版社获管理的取全部出版社、模糊查询、查看出版社、新增出版社、修改出版社功能都已经实现了,由于出版社与其他的表没有任何关联,因此实现起来都比较简单,业务层几乎不需要处理任何业务。和出版社管理相类似的书本种类管理,实现起来比出版社管理更加简单,同样地,书本类型管理也没有任何的外键关联,书本类型表的字段也比出版社表的字段少,因此可以按照同样的方法实现书本种类管理,为节省篇幅,本文省略书本类型管理模块。
4.5 书本管理模块
与出版社管理功能相比,书本管理比较复杂,因为书与出版社、书本种类相关联,因此在操作书的同时,也需要考虑如何得到这两种数据。另外,书还包括图片,因此对图片的处理上还需要编写一些代码。除了书本记录有外键和需要有图片处理的功能外,其他功能都与出版社管理类似,交互方式也是大体一致的。
与出版社管理模块以及种类管理模块不同的是书本管理需要从外界获得出版社信息和所属种类信息,因此我们需要为Book类添加两个属性,分别是种类(Type)和出版社(Concern),代码如4.1中的Book类,然后在BookService的实现类中,我们需要将两个外键转换成两个对象,然后在显示层显示。
程序运行效果图如图4.3所示。
图4.3 出版社主页面
分步骤实现以下功能:
1.获取全部书功能。
数据层关键代码如下:
public class BookDaoImpl extends CommonDaoImpl implements BookDao {
public Collection<Book> findAll() {
//查找的SQL语句
String sql = "SELECT * FROM T_BOOK book ORDER BY book.ID desc";
//调用父类的方法返回数据
return getDatas(sql, new Vector(), Book.class);
}
}
业务层接口的代码:
private BookDao bookDao;
private TypeDao typeDao;
private ConcernDao concernDao;
//当构造BookServiceImpl的时候,需要三个接口
public BookServiceImpl(BookDao bookDao, TypeDao typeDao, ConcernDao concernDao) {
this.bookDao = bookDao;
this.typeDao = typeDao;
this.concernDao = concernDao;
}
//实现接口方法
public Collection<Book> getAll() {
Collection<Book> result = bookDao.findAll();
//调用setAssociate方法设置关联的两个对象
return setAssociate(result);
}
//设置关系对象
private Collection<Book> setAssociate(Collection<Book> result) {
//遍历结果集合,设置每一个书的对象
for (Book book : result) {
//查找出对应的种类,再为书设置种类对象
book.setType(typeDao.find(book.getTYPE_ID_FK()));
//查找出对应的出版社,再为书设置出版社对象
book.setConcern(concernDao.find(book.getPUB_ID_FK()));
}
return result;
}
显示层实现的关键代码:
private void setViewDatas() {
//查找对应的数据
Vector<Book> books = (Vector<Book>)bookService.getAll();
//转换显示格式
Vector<Vector> datas = changeDatas(books);
//调用父类方法设置结果集合
setDatas(datas);
}
//将数据转换成视图表格的格式
private Vector<Vector> changeDatas(Vector<Book> datas) {
Vector<Vector> view = new Vector<Vector>();
for (Book book : datas) {
Vector v = new Vector();
v.add(book.getID());
v.add(book.getBOOK_NAME());
v.add(book.getBOOK_INTRO());
v.add(book.getAUTHOR());
v.add(book.getType().getTYPE_NAME());
v.add(book.getConcern().getPUB_NAME());
v.add(book.getREPERTORY_SIZE());
v.add(book.getBOOK_PRICE());
view.add(v);
}
return view;
}
2.查看书本功能
显示层关键代码:
//刷新图片显示的JLabel
private void refreshImage() {
this.imageLabel.setIcon(this.currentImage);//imageLabel为图片显示的JLabel
}
//查看书本
private void view() {
String id = getSelectId(getJTable());
Book book = bookService.get(id);
//设置各个对应区域的值
this.bookId.setText(book.getID());
this.bookName.setText(book.getBOOK_NAME());
this.price.setText(book.getBOOK_PRICE());
this.intro.setText(book.getBOOK_INTRO());
this.author.setText(book.getAUTHOR());
this.typeComboBox.setSelectedItem(makeType(book.getType()));
this.concernComboBox.setSelectedItem(makeConcern(book.getConcern()));
this.currentImage = new ImageIcon(book.getIMAGE_URL());
this.currentImagePath = book.getIMAGE_URL();
//重新设置当前显示的图片
refreshImage();
//创建一个Type对象, 用于添加到下拉框中, 该方法中创建的Type对象重写了toString和equals方法
private Type makeType(final Type source) {
Type type = new Type(){
public String toString(){
return source.getTYPE_NAME();
}
public boolean equals(Object obj) {
if (obj instanceof Type) {
Type t = (Type)obj;
if (getID().equals(t.getID())) return true;
}
return false;
}
};
type.setID(source.getID());
return type;
}
//从数据库中获取全部的种类并添加到下拉框中
private void addTypes() {
//调用种类业务接口取得全部的种类
Collection<Type> types = this.typeService.getAll();
for (Type type : types) {
//typeComboBox是种类下拉框对象
this.typeComboBox.addItem(makeType(type));
}
}
3.新增书本功能
数据层关键代码:
//实现接口BookDao的add方法
public String add(Book book) {
//根据book对象拼装SQL
String sql = "INSERT INTO T_BOOK VALUES (ID, '" + book.getBOOK_NAME() + "', '" + book.getBOOK_INTRO() + "', '" + book.getBOOK_PRICE() + "', '" + book.getTYPE_ID_FK() + "', '" + book.getPUB_ID_FK() + "', '"+book.getIMAGE_URL() + "', '" + book.getAUTHOR() + "', '" + book.getREPERTORY_SIZE() + "')"; //执行SQL并返回ID
return String.valueOf(getJDBCExecutor().executeUpdate(sql));
}
业务层关键代码:
//实现BookService的add方法
public Book add(Book book) {
String id = bookDao.add(book);
return get(id);
}
显示层关键代码:
//从界面中获取数据并封装成Book对象
private Book getBook() {
String bookName = this.bookName.getText();
String price = this.price.getText();
String intro = this.intro.getText();
String author = this.author.getText();
//从种类下拉框中取得对应的种类
Type type = (Type)this.typeComboBox.getSelectedItem();
//从出版社下拉框中取得相应的出版社
Concern concern = (Concern)this.concernComboBox.getSelectedItem();
//this.currentImagePath是当前显示的图片路径
return new Book(null, bookName, intro, price, type.getID(),
concern.getID(), String.valueOf(0), this.currentImagePath, author);
}
//新增书本
private void add() {
//从界面中取得书本的值并封装成Book对象,新增书本时库存为0
Book book = getBook();
bookService.add(book);
//重新读取数据
setViewDatas();
//刷新列表, 清空表单
clear();
}
4.图片上传功能
界面提供一个选择图片的按钮,可以提供给用户选择书的图片,在本例中,不必做得太复杂,因此一本书只有一张图片。在用户选择了图片后,我们可以将用户所选择的图片复制到我们的项目目录中,并再生成一张对应的缩略图,缩略图在书本管理界面显示,当点击了缩略图时,可以显示原图给用户浏览。先编写一个ImageUtil类,用来专门处理图片。关键代码如下:
//压缩图片的最大宽
public final static int MAX_WIDTH = 220;
//压缩图片的最大高
public final static int MAX_HEIGHT = 240;
/**
* 经过原图片的压缩后, 生成一张新的图片
* @param imageFile 图片文件
* @param url 原图相对路径
* @param formatName 生成图片的格式
* @param compress 是否进行压缩
* @return 生成的图片文件
*/
public static File makeImage(File imageFile, String url, String formatName,
boolean compress) {
File output = new File(url);
//读取图片
BufferedImage bi = ImageIO.read(imageFile);
//如果不进行压缩, 直接写入文件并返回
if (!compress) {
ImageIO.write(bi, formatName, new FileOutputStream(output));
return output;
}
//获得新的Image对象
Image newImage = getImage(bi);
//获取压缩后图片的高和宽
int height = newImage.getHeight(null);
int width = newImage.getWidth(null);
//以新的高和宽构造一个新的缓存图片
BufferedImage bi2 = new BufferedImage(width, height,BufferedImage.
TYPE_INT_RGB);
Graphics g = bi2.getGraphics();
//在新的缓存图片中画图
g.drawImage(newImage, 0, 0, null);
//输出到文件
ImageIO.write(bi2, formatName, new FileOutputStream(output));
return output;
}
//返回一个Image对象,当参数bi的宽比高大的时候,使用最大的宽来压缩图片。
//当参数bi的高比宽大的时候,使用最大的高来压缩图片
private static Image getImage(BufferedImage bi) {
if (bi.getWidth() > bi.getHeight()) {
return bi.getScaledInstance(MAX_WIDTH, -10,
Image.SCALE_AREA_AVERAGING);
} else {
Return bi.getScaledInstance(MAX_WIDTH, -10, Image.SCALE_AREA_AVERAGING); }
}
//生成uuid,作为上传的文件名
public static String getUUID() {
UUID uuid = UUID.randomUUID();
return uuid.toString();
}
显示层关键代码:
class FileChooser extends JFileChooser {
//书本管理界面对象
BookPanel bookPanel;
public FileChooser(BookPanel bookPanel){
this.bookPanel = bookPanel;
}
//选择了文件后触发
public void approveSelection() {
File file = getSelectedFile();//获得选择的文件
this.bookPanel.upload(file); //调用书本管理界面对象的upload方法
super.approveSelection();
}
}
//上传图片
public void upload(File selectFile) {
String uuid = ImageUtil.getUUID();
String smallFilePath = "upload/" + uuid + ".jpg";//缩略图的url
String bigFilePath = "upload/" + uuid + "-big.jpg";//原图的url
File file = ImageUtil.makeImage(selectFile, smallFilePath, "jpg", true); //生成缩略图
File source = ImageUtil.makeImage(selectFile, bigFilePath, "jpg", false); //生成原图
this.currentImage = new ImageIcon(file.getAbsolutePath());//设置界面显示的图片对象
this.currentImagePath = smallFilePath; //设置界面显示的图片url
refreshImage();//刷新图片显示区
}
upload方法中,需要生成两张图片,一张是原图的副本,与原图一致,另外一张是缩略图,缩略图的高和宽都不大于图片显示区的高和宽。
如果需要浏览原图,可以为图片显示区的JLabel添加监听器,当点击了该JLabel时,弹出一个JFrame,显示对应图片的原图。
与出版社管理,种类管理不同的是书本管理界面的业务逻辑层需要对数据进行处理而不仅仅是简单的从数据层获取数据。书本管理功能实现了获取全部书、查看书本、新增书本、图片上传与显示、书本修改四大功能。
4.6 销售管理模块
销售管理功能,交互方式与前面已经完成的功能类似,但是在表单处理的时候就有所不同,由于一条销售记录可能涉及多本书,因此,在表单中需要有书本交易记录的列表。另外,在为书本交易记录列表增加或者删除一条记录的时候,系统还要为总价和购买数量进行计数量,并显示到相应的地方。当保存销售记录的时候,就需要先保存销售记录,再逐条保存书的销售记录。
程序运行效果图如图4.4所示。
图4.4 销售模块主页面
分步骤实现以下功能:
1.销售记录列表功能:
为SaleRecord添加属性:
private int amount; //销售的总数量
private double totalPrice; //总价钱
private Vector<BookSaleRecord> bookSaleRecords; //书的销售记录
private String bookNames; //该记录中对应所有书的名称, 显示用
实现数据层的关键代码:
public class BookSaleRecordDaoImpl extends CommonDaoImpl implements
BookSaleRecordDao {
//根据销售记录id获得书的销售记录集合
public Collection<BookSaleRecord> findBySaleRecord(String saleRecordId) {
Stringsql="SELECT*FROM_BOOK_SALE_RECORDrWHERE
r.T_SALE_RECORD_ID_FK='" +saleRecordId + "'";
return getDatas(sql, new Vector(), BookSaleRecord.class);
}
}
实现业务层的关键代码:
//实现接口方法
public Collection<SaleRecord> getAll(Date date) {
//得到下一天
Date nextDate = DateUtil.getNextDate(date);
//得到今天的日期, 格式为yyyy-MM-dd
String today = DateUtil.getDateString(date);
//得到明天的日期, 格式为yyyy-MM-dd
String tomorrow = DateUtil.getDateString(nextDate);
Collection<SaleRecord> record=saleRecordDao.findByDate(today, tomorrow);
for (SaleRecord r : records) {
processDatas(r);
}
return records;
}
//处理一个SaleRecord, 设置它的书本销售记录属性和书本名字属性
private SaleRecord processDatas(SaleRecord r) {
//查找该记录所对应的书的销售记录
Collection<BookSaleRecord> brs = bookSaleRecordDao.findBySaleRecord
(r.getID());
//设置结果集中的每一个book属性
setBook(brs);
//设置SaleRecord对象中的书的销售记录集合
r.setBookSaleRecords((Vector<BookSaleRecord>)brs);
//设置SaleRecord的书名集合
r.setBookNames(getBookNames(brs));
//设置数量与总价
r.setAmount(getAmount(brs));
r.setTotalPrice(getTotalPrice(brs));
return r;
}
//获取一次交易中涉及的总价
private double getTotalPrice(Collection<BookSaleRecord> brs) {
double result = 0;
for (BookSaleRecord br : brs) {
//书本的交易量
int tradeSum = Integer.valueOf(br.getTRADE_SUM());
//书的单价
double bookPrice = ouble.valueOf(br.getBook().getBOOK_PRICE());
result += (bookPrice * tradeSum);
}
return result;
}
//获取一次交易中所有书本的交易量
private int getAmount(Collection<BookSaleRecord> brs) {
int result = 0;
//遍历书的交易记录,计算总价
for (BookSaleRecord br : brs) {
result += Integer.valueOf(br.getTRADE_SUM());
}
return result;
}
//设置参数中的每一个BookSaleRecord的book属性
private void setBook(Collection<BookSaleRecord> brs) {
for (BookSaleRecord br : brs) {
//调书本的数据访问层接口
Book book = bookDao.find(br.getBOOK_ID_FK());
br.setBook(book);
}
}
//获取一次交易中所有书本的名字, 以逗号隔开
private String getBookNames(Collection<BookSaleRecord> brs) {
if (brs.size() == 0) return "";
StringBuffer result = new StringBuffer();
for (BookSaleRecord br : brs) {
Book book = br.getBook();
result.append(book.getBOOK_NAME() + ", ");
}
//去掉最后的逗号并返回
return result.substring(0, result.lastIndexOf(","));
}
实现显示层的关键代码:
//将数据转换成主列表的数据格式
private Vector<Vector> changeDatas(Vector<SaleRecord> records) {
Vector<Vector> view = new Vector<Vector>();
for (SaleRecord record : records) {
Vector v = new Vector();
v.add(record.getID());
//得到一次交易中所有书的名字
v.add(record.getBookNames());
//得到交易的总价
v.add(record.getTotalPrice());
v.add(record.getRECORD_DATE());
//得到交易总数
v.add(record.getAmount());
view.add(v);
}
return view;
}
2.查看交易记录
查看交易记录,与查看出版社、查看书本等功能类似,只是需要处理书的销售记录的列表显示,即每一次交易记录中所涉及的多本书的记录,都需要在下面的列表显示。先实现数据访问层和业务层,得到SaleRecord对象,然后调用processDatas方法对单个SaleRecord进行值的处理,分别设置书的名称、交易总价和交易数量,最后返回SaleRecord对象。以下省略查找SaleRecord的数据访问层方法和业务层方法,直接对界面进行处理。关键代码如下:
//刷新书本销售记录的列表
private void refreshBookSaleRecordTableData() {
Vector<Vector> view = changeBookSaleRecordDate(this.bookSaleRecordDatas);
DefaultTableModel tableModel=(DefaultTableModel)this.bookSaleRecordTable.
getModel();
//将数据设入表格Model中
tableModel.setDataVector(view, this.bookSaleRecordColumns);
//设置表格样式
setBookSaleRecordTableFace();
}
//查看一条销售记录
private void view() {
String saleRecordId = getSelectId(getJTable());
//得到书的交易记录
SaleRecord record = saleRecordService.get(saleRecordId);
//设置当前书本销售数据
this.bookSaleRecordDatas = record.getBookSaleRecords();
//刷新书本销售列表
refreshBookSaleRecordTableData();
this.saleRecordId.setText(record.getID());
this.totalPrice.setText(String.valueOf(record.getTotalPrice()));
this.recordDate.setText(record.getRECORD_DATE());
this.amount.setText(String.valueOf(record.getAmount()));
}
//设置书本销售记录的样式
private void setBookSaleRecordTableFace() {
this.bookSaleRecordTable.setRowHeight(30);
//隐藏销售记录id列
this.bookSaleRecordTable.getColumn("id").setMinWidth(-1);
this.bookSaleRecordTable.getColumn("id").setMaxWidth(-1);
//隐藏对应的书id列
this.bookSaleRecordTable.getColumn("bookId").setMinWidth(-1);
this.bookSaleRecordTable.getColumn("bookId").setMaxWidth(-1);
}
//将书的销售记录转换成列表格式
private Vector<Vector> changeBookSaleRecordDate(Vector<BookSaleRecord> records) {
Vector<Vector> view = new Vector<Vector>();
for (BookSaleRecord r : records) {
Vector v = new Vector();
v.add(r.getID());
v.add(r.getBook().getBOOK_NAME());
v.add(r.getBook().getBOOK_PRICE());
v.add(r.getTRADE_SUM());
v.add(r.getBook().getID());
view.add(v);
}
return view;
}
//向列表添加一条书的销售记录
private void appendBook() {
//获得选中的对象
Book book = (Book)bookComboBox.getSelectedItem();
String amount = this.bookAmount.getText();
appendOrUpdate(book, amount);
//刷新列表
refreshBookSaleRecordTableData();
//计算总价
countTotalPrice();
//计算总数量
setTotalAmount();
}
//向书的销售记录列表中删除或者添加一本书的时候计算总价
private void countTotalPrice() {
double totalPrice = 0;
for (BookSaleRecord r : this.bookSaleRecordDatas) {
totalPrice += (Integer.valueOf(r.getTRADE_SUM()) *
Double.valueOf(r.getBook().getBOOK_PRICE()));
}
this.totalPrice.setText(String.valueOf(totalPrice));
}
//向书的销售记录列表中删除或者添加一本书的时候设置总数量
private void setTotalAmount() {
int amount = 0;
for (BookSaleRecord r : this.bookSaleRecordDatas) {
amount += Integer.valueOf(r.getTRADE_SUM());
}
this.amount.setText(String.valueOf(amount));
}
//添加或者修改书本交易记录中的对象
private void appendOrUpdate(Book book, String amount) {
BookSaleRecord r = getBookSaleRecordFromView(book);
//如果为空, 则为新添加的书, 非空, 则该书已经在列表中
if (r == null) {
//创建BookSaleRecord对象并添加到数据集合中
BookSaleRecord record = new BookSaleRecord();
record.setBook(book);
record.setTRADE_SUM(amount);
this.bookSaleRecordDatas.add(record);
} else {
int newAmount = Integer.valueOf(amount) + Integer.valueOf
(r.getTRADE_SUM()); r.setTRADE_SUM(String.valueOf(newAmount));
}
}
//从列表中移除一条书的销售记录
private void removeBook() {
//在集合中删除对应的索引的数据
this.bookSaleRecordDatas.remove(bookSaleRecordTable.getSelectedRow());
//刷新列表
refreshBookSaleRecordTableData();
//重新计算总价和总数量
setTotalAmount();
countTotalPrice();
}
3.新增交易记录功能
数据层关键代码:
public String save(SaleRecord r) {
//执行SQL写入数据
String sql="INSERT INTO T_SALE_VALUES(ID,'"+r.getRECORD_DATE()+'")"; return String.valueOf(getJDBCExecutor().executeUpdate(sql));
}
public String saveBookSaleRecord(BookSaleRecord record) {
//保存一条书的销售记录
String sql="INSERT INTO T_BOOK_SALE_RECORD VALUES(ID, '"+record.getID()+
'",'"+record.getT_SALE_RECORD_ID_FK()+'",'"+record.getTRADE_SUM()+'")"
return String.valueOf(getJDBCExecutor().executeUpdate(sql));
}
业务逻辑层关键代码:
public void saveRecord(SaleRecord record) {
//遍历判断书的库存是否不够
for (BookSaleRecord r : record.getBookSaleRecords()) {
String bookId = r.getBook().getID();
Book b = bookDao.find(bookId);
//当存库不够时,抛出异常
if (Integer.valueOf(r.getTRADE_SUM()) >
Integer.valueOf(b.getREPERTORY_SIZE())) {
throw new BusinessException(b.getBOOK_NAME() + " 的库存不够");
}
}
//先保存交易记录
String id = saleRecordDao.save(record);
//再保存书的交易记录
for (BookSaleRecord r : record.getBookSaleRecords()) {
//设置销售记录id
r.setT_SALE_RECORD_ID_FK(id);
bookSaleRecordDao.saveBookSaleRecord(r);
//修改书的库存
String bookId = r.getBook().getID();
Book b = bookDao.find(bookId);
//计算剩余的库存
int leave = Integer.valueOf(b.getREPERTORY_SIZE()) -
Integer.valueOf(r.getTRADE_SUM());
//设置库存并将库存数保存到数据库
b.setREPERTORY_SIZE(String.valueOf(leave));
bookDao.updateRepertory(b);
}
}
以上代码保存了销售记录并修改书本的库存量,在进行保存销售记录前,需要进行业务判断,判断书本的存库量是否足够,如果不足够则抛出异常。
到此,销售界面的功能已经完成,与其他功能相比,销售管理界面只是多了一个列表,该列表的处理方法与主列表的处理方式大致相同,都是通过相同的交互方式得到销售或者入库的目的,但是与之不同的地方是,在处理入库业务的时候,我们同样也需要修改书本的库存,销售是减少库存,入库是增加库存。与销售管理功能一样的是,入库管理界面的下面部分也是有一个入库的书的记录列表,原理与书的交易记录列表一致。
5 系统测试
5.1 系统测试目的与意义
省略
5.2 测试过程
测试的过程就是把一切可能的因素考虑在内,按照“最不利”原则去进行测试,按照这样的思路尽可能多的设计不重复的用例,这样就可以在将系统交到客户手中之前使系统更加完善起来,减少了系统维护成本。
5.2.1 登录模块测试
测试流程: 1.打开系统首页,输入错误的登录信息
2.登录
3.输入正确的登录信息
4.登录
5.测试结果
表5.1 登录模块测试用例表
5.2.2 销售管理模块测试
测试流程: 1.打开销售界面,输入错误的数量
2.交易
3.输入正确的数量信息
4.交易
5.测试结果
表5.2 销售管理模块测试表
5.2.3 库存管理模块测试
测试流程: 1.打开库存管理界面,输入错误数量信息
2.入库
3.输入正确的数量信息
4.入库
5.测试结果
表5.3 库存管理模块测试表
5.2.4 书本管理模块测试
由于书本管理模块中的书本名称、作者、简介等信息是用varchar定义,所以输入不存在判断,因此没有必要对其进行测试,只需要对价格测试。
测试流程: 1.打开书本管理界面,输入错误的价格信息
2.保存
3.输入正确价格信息
4.保存
5.测试结果
表5.4 书本管理模块测试表
结 论
省略
参考文献
[1] 葛欣,孟凡荣. 使用cU/GUI开发图形用户界面[J]. 计算机工程与设计, 2005
[2] 陈一明. SQL Server数据库应用技巧探讨[J].科学技术与工程, 2008
[3] 张军峰.如何使软件测试更有效[J].电脑知识与技术, 2005
[4] 王世峰. MVC设计模式的研究与应用[D].中国海洋大学,2006
[5] 赵淑菊. Java GUI事件处理机制之探究[J]. 济南职业学院学报, 2010
[6] 吕校春,李玲莉.基于Swing的Java GUI组件开发[J].机械工程师,2008.
[7] 潘红改,李国贞.Java GUI布局管理方法探讨[J]. 漯河职业学院学报, 2010.
[8] Crosby, Philip B. Quality is Free. New York: New American Library, Mentor Books, 1979.
[9] Chelf, Ben and Raoul Jetley. “Diagnosing Medical Device Software Defects Using Static Analysis.” Coverity Technical Report. San Francisco: 2008.
[10] W.Clay,Richardson,Donald,“Avondolio. The Java high class weaves a distance:JDK 5”, Scientific & Technology Book Review,No.3,2006.
[11] Andrea Adamoli,Dmtrijs Zapranuks,Milan Jovic,Mathias Hauswirth.Automated
GUI performace testing. Software Quality Journal,2011
致 谢
省略
英文文献原文
省略
中文译文
省略