图书进销存系统(CS) 联系客服

发布时间 : 星期三 文章图书进销存系统(CS)更新完毕开始阅读4758ae4f2e3f5727a5e96277

第9章 图书进存销系统

for (Field field : allFields) { //获得setter方法的方法名

String setterMethodName = getSetterMethodName(field.getName()); //获得setter方法

·17·

Method setterMethod = clazz.getMethod(setterMethodName, field.getType()); invokeMethod(rs, field, vo, setterMethod); } result.add(vo); } rs.close(); return result; }

//执行一个方法, 从ResultSet中获取一个字段的数据, 调用vo的setter方法

private static void invokeMethod(ResultSet rs, Field field, Object vo, Method setterMethod) { //当使用ResultSet获取某个字段的时候, 如果没有该字段, 会出现SQLException, 在这里忽略该异常

String value = rs.getString(field.getName());

//从ResultSet中获取与该对象属性名一致的字段, 并执行setter方法

setterMethod.invoke(vo, value); }

//根据属性名获得setter方法的方法名

private static String getSetterMethodName(String fieldName) { String begin = fieldName.substring(0, 1).toUpperCase(); String end = fieldName.substring(1, fieldName.length()); String methodName = \ return methodName; }

//相加两个数组

private static Field[] addFields(Field[] f1, Field[] f2) { List l = new ArrayList(); for (Field f : f1) l.add(f); for (Field f : f2) l.add(f); return l.toArray(new Field[f1.length + f2.length]); }

注意以上代码的黑体部分,先使用class的newInstance方法创建类的实例,再获得父类和本类的属性,再通过setter方法将ResultSet中对应的值设置到对象中。我们在9.4.1中,约定了各个类的属性命名都需要与数据库中的表字段一致,在这里,我们很容易就可以得到某个字段的setter方法,并可以根据类的属性名称得到ResultSet中对应的字段值。在本例中,父类就是ValueObject,ValueObject中有一个ID属性,就对应了各个表中的ID字段。ValueObject下面的各个子类的class都可以作为getDatas的clazz参数传入,从而创建传入类的对象。

简单的说,DataUtil就是根据ResultSet来创建ValueObject的集合,当创建完后,就需要在这里关闭ResultSet对象。这也是为什么在JDBCExecutor不需要关闭ResultSet的原因。

到这里,开发的准备工作已经全部完成了,在本小节中,描述了如何连接JDBC进行数据库连接,使用Java的反射进行字段与类属性的映射,这些都为我们后面的功能开发打好了基础,在开发的过程中,我们可以直接编写SQL,得到结果集,传递给DataUtil类,让它转换成集合,我们不再需要去处理ResultSet、Connection和Statement等对象。

·18· 第9章 图书进存销系统

9.5 出版社管理功能

从本小节开始,我们开始实现系统功能,从最简单的出版社管理功能开始,由于出版社表中不涉及任何的外键,界面中只有一个简单的列表和表单,实现起来比较简单。

9.5.1 分层结构

我们可以将系统分为三层:表现层、业务层、数据访问层,这样分层的好处在于,如果视图层发生变化,例如不再使用swing作为表现层,使用jsp的话,那么,业务层、数据访问层的代码将不需要改变,达到重用的目的。业务层与数据访问层分别提供各自的接口,在表现层中使用业务层的接口,业务层中使用数据访问层的接口,就算实现发生了改变,也可以不用去更改调用者的代码,当需要更改某一部分实现的时候,直接更换实现类即可。

我们现在开始开发数据访问层的代码,先数据访问层建立一个父类,给该层各个DAO实现类去继续,提供一些可能每个DAO都会使用到的方法。新建CommonDaoImpl对象,具体代码如下。

代码清单:code\\book\\src\\org\\crazyit\\book\\dao\\impl\\CommonDaoImpl.java

public class CommonDaoImpl { //返回JDBCExecutor对象 }

public JDBCExecutor getJDBCExecutor() { return JDBCExecutor.getJDBCExecutor(); }

//根据参数的SQL, 存放结果的集合对象, 和具体的数据库映射对象返回一个集合 public Collection getDatas(String sql, Collection result, Class clazz) { //执行SQL返回ResultSet对象 }

ResultSet rs = getJDBCExecutor().executeQuery(sql); //对ResultSet进行封装并返回集合 return DataUtil.getDatas(result, rs, clazz);

那么在DAO中每个实现类去继承CommonDaoImpl的话,即可使用getDatas方法返回已经封装好的数据集合。

出版社数据访问层接口:

public interface ConcernDao { //提供需要实现的接口方法 }

出版社业务接口:

public interface ConcernService { //提供需要实现的接口方法 }

那么在表现层的JPanel中,我们可以在JPanel的构造器中,将ConcernService作为构造参数传入到ConcernPanel(出版社管理界面)中。同样的,在ConcernService实现类的构造器中,将ConcernDao传入。

9.5.2 获取全部出版社

新建ConcernDao的实现类(数据访问层的实现类)ConcernDaoImpl,为ConcernDao添加一个

第9章 图书进存销系统 ·19·

需要实现的方法findAll,以下为findAll的接口方法及期实现。

ConcernDao,代码清单:code\\book\\src\\org\\crazyit\\book\\dao\\ConcernDao.java

public interface ConcernDao { //查找全部的出版社并返回集合 }

Collection findAll();

ConcernDaoImpl,代码清单:code\\book\\src\\org\\crazyit\\book\\dao\\impl\\ConcernDaoImpl.java

public class ConcernDaoImpl extends CommonDaoImpl implements ConcernDao { public Collection findAll() { //查找的SQL }

}

String sql = \

//调用父类的getDatas方法返回集合, 返回的类型是集合, 集合里面存放的类型是Concern return getDatas(sql, new Vector(), Concern.class);

注意以上代码中的黑体部分,调用父类的方法返回数据集合,返回怎样的数据集合,根据参数类型决定。在这里,就可以体会到在9.4中所做的准备工作的重要性,我们不需要再去处理ResultSet对象,即可将结果封装成集合,减低了代码的耦合。

接下来,再去实现业务层:

ConcernService,代码清单:code\\book\\src\\org\\crazyit\\book\\service\\ConcernService.java

public interface ConcernService { //获取全部的出版社 }

Collection getAll();

ConcernServiceImpl。

代码清单:code\\book\\src\\org\\crazyit\\book\\service\\impl\\ConcernServiceImpl.java

public class ConcernServiceImpl implements ConcernService { //数据访问对象 }

private ConcernDao dao;

//在构造器中设置ConcernDao

public ConcernServiceImpl(ConcernDao dao) { this.dao = dao; }

//直接返回数据

public Collection getAll() { return dao.findAll(); }

注:在业务层中,并没有对结果集合进行任何处理,使用数据访问对象得到数据后马上返回,这是由于我们这里并没有任何复杂的业务,只需要得到数据即可。

如果需要进行一些业务的处理,那么可以在业务层的实现类中进行处理。例如,如果某天有一个需求,出版社名字中含有英文字母的出版社不显示到表现层中,那么负责处理这个需求的代码,就应该写在业务层中。

接下来,就是表现层如何得到数据并进行显示了。

代码清单:code\\book\\src\\org\\crazyit\\book\%ui\\ConcernPanel.java

//将数据转换成视图表格的格式

private Vector changeDatas(Vector datas) { Vector view = new Vector(); for (Concern c : datas) {

·20·

第9章 图书进存销系统

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; }

//实现父类方法,查询数据库并返回对应的数据格式, 调用父类的setDatas方法设置数据集合 public void setViewDatas() { Vector concerns = (Vector)service.getAll();//使用业务接口得到数据 Vector datas = changeDatas(concerns); //使用业务接口得到数据 setDatas(datas); //调用父类的setDatas方法 }

注:在9.2.7中,我们将一些代码提到父类CommonPanel中,因此,在各个界面的构造器中,需要

调用setJTable方法,将子类创建的JTable设置到父类中。

我们在上面所编写的setViewDatas方法,同样的也需要在构造器中调用。 CommonPanel构造器:

public ConcernPanel(ConcernService service) { this.service = service; initColumns();//初始化列集合 setViewDatas();//设置列表数据 DefaultTableModel model = new DefaultTableModel(null, this.columns); //设置列表 }

JTable table = new CommonJTable(model); setJTable(table); //调用父类的setJTable方法 //省略其他界面代码

父类得到数据、JTable对象和列集合后,就可以根据这些对象来创建列表了。创建列表的方法主要是父类的initData方法,具体请看9.2.7章节。效果如图9.7所示。