项目简介
图书馆管理小项目的主要目的是让学完JavaSE的同学对之前掌握的知识做一些运用,该项目应用的知识点包括下面内容:
- 面向对象的思想
- 分层的思想
- 接口
- 异常
- 集合
- 日期处理
- Stream流
- IO流
- 反射
- Javafx(了解)
- css(了解)
通过学习本项目,可以巩固JavaSE的知识,对于后续的学习来说可以起到很好的衔接。
环境搭建
基本信息
- 开发工具:IDEA
- JDK版本:8
- 项目编码:GBK
使用技术
除了javase相关知识点外,该项目还使用了一些第三方的技术,包括:
- Jfoenix:提供了更加美观的UI控件
- Dashboardfx:将一些美观的UI控件组合起来使用,构建出了一个仪表盘
项目导入
因为实际应用中对javafx的使用很少,所以这部分的内容就不带着写了,了解即可。界面大部分代码已经写好,只需要将nodelibrary.zip文件解压导入到项目中即可。
关联第三方jar包
由于项目中使用了一些第三方的技术,所以需要将这些技术相关的jar包导入到项目来使用。
实际应用的时候,我们会使用很多第三方技术,这些第三方技术的开发者会将代码打成jar包以供使用。
代码解析
代码概览
- bean:存放实体类的包
- global:存放了一些全局使用的类
- media.img:存放一些图片
- module:存放界面相关的类
- service:存放服务相关类
- theme:存放美化界面相关的文件
- App:主类
登录功能
在项目启动时,会加载所有界面对应的fxml文件,然后将这些对象放入到map中,此map由ViewManager类进行管理。
用户登录后,会根据输入的用户名从properties文件中查找数据,如果查询出来的数据跟用户输入的用户名和密码匹配的话,则登录成功,跳转至main页面中。
fxml文件
每个界面都对应了一个fxml文件,fxml文件中的内容可以控制界面的展示效果。该文件中的标签都是成对出现的,比如:
<StackPane></StackPane>
有部分标签可以写成下面形式:
<button />
一个标签中可以包含其他标签,例如:
<StackPane> <padding> </padding> </StackPane>
但是绝对不能出现下面这种形式(你中有我,我中有你):
<StackPane> <padding> </StackPane> </padding>
上面写法会有问题。
绑定控制器
每个fxml文件都有一个对应的java文件相关联,这样的java文件叫做控制器,我们可以通过fxml文件中的最外层标签里面的fx:controller标签进行绑定:
fx:controller="com.bjpowernode.module.login.Login"
事件
用户在操作界面的时候会产生一些行为,比如点击一个按钮,这就是一个点击时间,我们可以通过button标签中的onAction属性与控制器对应的方法进行绑定:
<button onAction=”#login”>
输入框
<TextField /> :我们可以在输入框中看到自己输入的内容
<PasswordField />:我们不能在输入框中看到自己输入的内容
通过输入框中的fx:id与控制器中的属性进行管理,例如:
fxml文件的标签:
<TextField fx:id="username" />
控制器文件的属性:
@FXML private TextField username;
分层的思想
实际开发中会编写大量的代码,不同的代码解决的问题不一样,因此我们最好将这些代码进行分层存放,主要还是划分职责,这样有利于后期的扩展。
分工前:
分工后:
代码分层:
- controller(请求处理层):负责与界面数据进行交互处理
- service(业务逻辑层):负责业务逻辑相关处理
- DAO(数据持久层):负责数据持久化操作,DAO的全称是Data Access Object
用户相关功能
1、用户数据初始化
功能概述
创建若干User对象放入到List中,通过对象输出流将List对象写出到硬盘的User.txt文件中,从而达到持久化的操作。
Serializable接口
通过IO流将List对象数据持久化到硬盘的文件中,List中存放的数据是User类型,所以要让User类实现Serializable接口。倘若我们要对某个类的对象进行IO操作时,别忘了让这个类实现Serializable接口。
常量类
对于一些不会发生频繁修改的数据,我们通常会将其定义为常量,存放常量的类叫做常量类。这么做的好处是对这些数据进行同一管理,将来一旦发生改变,代码改动较小。
2、查询用户
功能概述
通过对象输入流将硬盘中User.txt文件里面的List对象读取到内存中,将该对象传到FXML中,从而展示出数据。
serialVersionUID
在User类实现Serializable接口后,最好添加serialVersionUID,这样做的好处就是在User对象已经被存储到硬盘文件之后,我们再修改User类属性时不会发生异常。便于对User对象版本的控制。
代码分层的使用
- controller: UserViewCtrl
- service:接口UserService,实现类UserServiceImpl
- dao:接口UserDao,实现类UserDaoImpl
这里通过接口的使用可以提高代码的可插拔性,提高了程序的可维护性。
3、添加用户
功能概述
在添加界面输入用户信息之后,点击提交按钮,会将用户对象传到service层,service层再调用DAO,在DAO中使用对象输入流将用户集合数据读到内存中,将新增的user对象存入集合里面,之后使用对象输出流将用户集合数据写出到硬盘文件中。
编号自增长
在创建类似User这样的类时,我们通常会在里面添加一个id编号属性,目的是作为该对象的唯一标识,便于查询,修改,删除操作。为了保证id编号不重复,这里通过程序控制id编号自动增长,在多线程环境下需要注意线程安全的问题。
4、修改用户
功能概述
点击修改按钮之后需要将当前选中的修改数据显示到修改界面中,修改界面里面要存储数据的编号id,这些数据传到DAO层之后,通过id在用户集合中找到相应的用户对象,从而进行修改操作。这里的修改界面跟添加界面使用的是同一个fxml
5、删除用户
功能概述
点击删除按钮之后将当前选中数据的id编号传到service层,service层将id传到DAO层,然后根据id从用户集合中找到对应的用户对象,将其从集合移除即可。需要注意的是用户类User中要重写hashcode和equals方法。
异常的抛出
我们在DAO层中使用了try catch对异常进行了捕获,倘若DAO层出现了异常,其上层的service和controller中是不知道的,这里最好在DAO层的catch里面再抛出异常,目的是通知上层这里有异常,上层代码获取到异常之后再进行后续的处理。
6、冻结用户
功能概述
点击冻结按钮之后,将当前选中的id编号传到DAO,然后再根据id从用户集合中找到对应用户对象,将该对象中的状态修改为冻结即可。
图书相关功能
1、图书数据初始化
功能概述
创建若干Book对象,将对象放入到List中,把List对象利用对象输出流写出到硬盘的文件中,总体功能与用户数据初始化操作类似。
泛型通配符
图书数据初始化和用户数据初始化类似,所以将两个方法进行重构,重构之后合并为一个方法。方法中添加两个形参,分别是String path(存放的路径)和List<?> list(存放的数据)。这里由于list中的泛型是不同的,所以不能在形参中填写具体的类型,这里我们使用了泛型通配符来解决该问题。
2、查询图书
功能概述
将硬盘文件中的图书数据查询并展示到页面中,整体跟之前的用户查询类似,只是多了条件查询,在点击查询按钮时,需要将输入的图书名称和isbn传入到DAO中。需要注意的是如果图书名称和isbn同时输入的话,在查询代码中先根据图书名称查询出图书数据bookList,之后在bookList的基础上再根据isbn进行条件查询。
3、添加图书
功能概述
将输入的图书数据放到Book对象中,之后将该Book对象利用对象输出流写出到硬盘文件中,需要手动设置一下id编号的自增操作。与用户添加类似
4、删除图书
功能概述
根据输入的图书编号,从图书数据中找到与之对应的图书对象,然后在图书集合对象中将该图书对象删除。
5、修改图书
功能概述
将修改之后的图书数据持久化到硬盘文件中,功能与修改用户类似。
反射的使用
修改操作中需要将输入的数据一一对应的放入到查询出的图书对象中,需要调用很多set和get方法,倘若属性过多的话,代码就显得繁琐了,为了解决该问题,我们编写了一个工具类,里面利用反射来获取类中的全部属性,之后再进行赋值操作。注意工具类中要排除不希望赋值的属性,例如:serialVersionUID。
代码:
/** * 对象属性值的拷贝 * @param origin * @param dest */ public static void populate(Object origin, Object dest) { try { //使用反射解决这个问题 //判断两个对象是否是同一类型 if (origin.getClass() != dest.getClass()) { throw new RuntimeException("两个对象必须得是同一类型"); } Class<?> clazz = origin.getClass(); //获取origin中的属性 Field[] fields = clazz.getDeclaredFields(); for (Field f : fields) { //排除serialVersionUID if ("serialVersionUID".equals(f.getName())) { continue; } //打破封装 f.setAccessible(true); //从dest对象中找到对应的属性值,然后赋值到origin相应的属性中 f.set(origin,f.get(dest)); } } catch (IllegalAccessException e) { e.printStackTrace(); } }
图书统计
1、功能概述
根据controller中的代码得出要想展示出图表内容,需要两个数据,分别是图书类型和该图书类型对应的书籍数量,至于图书展示的操作交给界面处理就行,因此我们要做的事就是从硬盘文件中图书书籍里面统计出不同类型书籍的数量分别有多少。
2、方法返回值
方法的返回值写什么要根据需求来定,因此在编写方法之前要搞明白当前的需求是要做什么,倘若在需求没有搞明白的前提下来编写代码,肯定会遇到很多问题。通过图书统计功能的编写我们要学会分析现有代码,根据代码得出所需要的数据,然后再自己操作获取这些数据即可,剩余数据展示的操作交给界面来处理即可。
借阅相关功能
1、借阅数据初始化
功能概述
利用之前编写的InitDataUtil工具类将借阅数据进行初始化。
UUID
由于可能会产生很多借阅数据,所以编号不能再用int类型自动增长了,这里将Lend类中的编号类型改成了String。这类的编号不要让用户来输入,我们使用UUID来生成。UUID的生成跟网卡MAC地址、时间戳、随机或伪随机数、时序等元素有关,可以保证唯一性。UUID的长度是36,去除里面的-之后长度是32。
2、借阅数据查询
功能概述
当用户借阅了某本书之后会生成一条数据,我们需要在借阅管理中将该数据查询出来,查询的时候需要注意Lend对象中包含了Book和User对象,对于这类对象的查询我们可以通过多次get方法将其查出。
3、借阅书籍
功能概述
通过点击图书管理中的借阅按钮生成一条借阅数据,在点击借阅按钮之后,需要查询出可以借书的用户,每位用户只能借阅1本书,这里查询出的用户列表必须都是可以借书的用户。这部分操作的界面较多,注意界面之间的数据传递。
User类的修改
为了能够方便的查询出哪些用书手中已经借书了,我们可以在User类中添加一个boolean属性isLend,该属性的作用是标识当前用户手中是否有书。但是修改User类之后会发现代码很多地方有编译报错,通过这个改动发现类似这种User的类最好在开始的时候定义好,否则后期修改会导致改动较大。
Service层的使用
Controller主要的作用是处理跟界面相关的操作,DAO层主要的作用是处理跟数据持久化方面的操作,遇到一些比较复杂的操作时,我们需要在service层编写业务逻辑代码,在service中可以调用多个不同得DAO。这样可以将代码分层处理,便于后期的扩展升级。
4、还书功能
功能概述
用户看完书籍之后需要进行归还,默认最长借阅周期是30天,倘若超出的话,超出1天扣1元,30元封顶。如果在还书的时候,用户的余额已为负数,则表示欠费了,需要改用户先还清欠款才能进行还书的操作。
5、逾期扣款
计算超出归还日期的用户所需支付的金额,然后进行扣款操作,这里个功能需要每天执行,我们可以使用定时任务解决,但是考虑到该项目用户不会每天运行,所以单独写个方法,当启动软件的时候进行同一计算然后再扣款。
6、充值
功能概述
用户欠费后需要充值并且充值之后的余额要大于0才能进行还书操作,我们需要精确计算出用户的余额,因此使用的类型是BigDecimal。
最后,想深入学习图书管理系统,点击以下链接
在线观看:https://www.bilibili.com/video/bv1tV411J77q
资料下载:http://www.bjpowernode.com/javavideo/210.html?zh91