为了使设计与实现相分离,以及更好的解耦合,我们给上面的项目加入接口,并且重构部分代码。
图Service层与Dao层
分析之前的代码,可以发现数据访问层中,存在着大量的重复代码。我们可以把重复的代码提取出来,封装到一个公共的类中(帮助类),从而实现代码的重构。为此,我们再创建一个数据访问层的帮助类DBUtil.java,并放入util包中。
org.lanqiao.DBUtil.java
可以发现,虽然将访问数据库的方法都到了一个DBUtil类中,但该类中的代码仍有较多的重复。例如,每个方法都需要加载数据库驱动类,并获取数据库连接对象、创建PreparedStatement对象等。为此,我们可以再次重构这些重复的代码,并将释放操作单独写在一个closeAll()方法中,做进一步简化,如下,
org.lanqiao.DBUtil.java
说明:
1问:数据访问层DAL中的某一个DAO实现类(如StudentDaoImpl.java),是用于访问数据库;而数据库帮助类DBUtil.java也是用于访问数据库。二者有什么区别?
答:StudentDaoImpl.java这种DAO实现类的操作对象是Student,即对Student进行增删改查等数据访问;而DBUtil.java中的增删改查方法都是通用的,不针对于任何一个实体。一般来讲,DAO实现类可以通过调用DBUtil.java中的方法,来实现对某一个特定实体(如Student)的增删改查等操作,详见后文中StudentDaoImpl.java中的代码。
2问:查询方法executeQuery()的返回值类型为什么是ResultSet,而不是List。例如,查询方法能否写成以下形式:
答:对于查询方法,List类型的返回值看起来确实比ResultSet更加直观,甚至在使用上更加方便。但是考虑到工具类中方法的“通用性”,就只能使用ResultSet作为返回值类型。例如,对于增删改的通用方法executeAddOrUpdateOrDelete(String sql ,Object[] os)来说,只要传入用于增删改的SQL语句(sql参数)用于替换SQL语句中所有?占位符的数组(os参数),就能够实现相应SQL语句所代表的增删改功能。也就是说,executeAddOrUpdateOrDelete()不依赖任何“实体”,无论是增删改“学生”,还是增删改“班级”、“图书”、“新闻”等,都可以通过调用该方法实现。但是,如果查询方法的返回值是List,并且方法中存在rs.getInt("stuNo")等代码,那么该查询方法就强烈依赖于Student类,而不能像增删改方法那样实现“通用性”。因此,为了查询方法的“通用性”,我们只能退一步,让查询方法仅返回结果集ResultSet就可以了,至于实体类数据的封装,以及List集合的填充等操作就只能转交到DAO实现类去完成。
有了DBUtil.java之后,数据库访问层中实现类的代码就简单许多了(其它各层的代码不变),如下,
优化后的三层架构的结构图,如图所示。
图优化后的三层结构图
从图中可以发现,优化后的三层架构的基本结构是:
表示层(USL前台Jsp或Html,及USL后台Servlet)+业务逻辑层(接口,及实现类)+数据访问层(接口,及实现类)+工具类包+实体类包
其中表示层前台,也可以加入CSS、JavaScript等进行美化和验证。
领取专属 10元无门槛券
私享最新 技术干货