软件设计-DAO层如何设计(长文-转)

此文章是本人学习mldn魔乐科技DAO设计模式教学视频后,总结下来,用于复习使用,在这里感谢mldn的学习视频。
DAO设计模式?
DAO全称是(Data Access Object,数据库访问对象),主要功能就是用于进行数据操作的,在程序的标准开发架构中属于数据层的操作。
简要介绍一下企业分层架构:
软件设计-DAO层如何设计(长文-转)
文章图片
image 显示层:主要使用JSP/Servlet进行页面效果的显示
业务层:(Business Object,数据对象)会将多个原子性的DAO操作进行组合,组合成一个完整的业务逻辑。
数据层:(DAO,Data Access Object,数据库访问对象)提供多个原子性的DAO操作,例如:增、删、改、查,都是原子性操作。
在整个业务中,最重要的是后台业务层。那什么是业务层?什么是数据层呢?
下面用个例子展示:
软件设计-DAO层如何设计(长文-转)
文章图片
image 业务层是整个程序提供的操作功能,而一个业务层的操作要想完成需要多个数据层的操作一起共同完成。
整个过程之中,发现数据层完成的只是一个个原子性的数据库开发。而在实际开发之中,每一个操作的业务往往需要牵扯到多个原子性的操作,也就是说所有的原子性的操作业务
最终在业务层中完成。
在实际的开发之中,业务的设计是非常复杂的,本次的操作知识简单的区分了业务层与数据层的基础关系,如果你的业务非常复杂(如银行项目),往往需要一个总业务层,而
后会牵扯到若干个子业务层,每一个子业务层又去执行多个数据层。
具体有多复杂呢?看看下面这个例图:
软件设计-DAO层如何设计(长文-转)
文章图片
image 对于数据层和业务层,可以概括成如下。
数据层:又被称为数据访问层(Data Access Object,DAO),是专门进行数据库的原子性操作,也就是说在数据层中最需要控制的就是JDBC中的PreparedStatement接口的使用;

**业务层**:又被称为业务中心,业务对象(Business Object,BO),但是现在又有一部分认为应该将其称为服务层(Service),业务层核心的目的是调用多个数据层的操作以完成整体的项目的业务设计,这个是整个项目的核心所在。

实例分析:
现在要求使用emp表(empno、ename、job、hiredate、sal、comm)实现如下的操作功能
(客户所提供的所有的需求都叫做业务)
-【业务层】实现雇员数据的添加,但是需要保证被添加的雇员编号不会重复;
【数据层】判断要增加的雇员编号是否存在;
【数据层】如果雇员编号不存在则进行数据的保存操作;
-【业务层】实现雇员数据的修改操作;
【数据层】执行数据的修改操作
-【业务层】实现多个雇员数据的删除操作;
【数据层】执行雇员的限定删除操作;
-【业务层】可以根据雇员编号查找到一个雇员的信息;
【数据层】根据雇员编号查询指定的雇员数据;
-【业务层】可以查询所有雇员的信息;
【数据层】查询全部雇员的数据;
-【业务层】可以实现数据的分页显示(模糊查询),同时又可以返回所有的雇员数量。
【数据层】雇员数据的分页查询;
【数据层】使用COUNT()函数统计出所有的雇员数量。
结论:用户所提出的所有的需求都应该划分为业务层,因为他指的是功能,而开发人员必须要根据业务层去进行数据层的设计。
软件设计-DAO层如何设计(长文-转)
文章图片
image 额外话题:从最早的DAO设计模式来讲实际上还会考虑到一个问题,多数据库的移植问题。
此时就需要设置一个专门的表示连接标准的接口。
软件设计-DAO层如何设计(长文-转)
文章图片
image 考虑到显示的开发之中,所提供的第三方框架平台越来越完善,所以以上的复杂设计也就慢慢的被忽略了。
定义DatabaseConnection类
整个的操作过程之中,DatabaseConnection只是无条件的提供有数据库连接,而至于说有没有连接对象,它都不关心。
范例:定义DatabaseConnection.java
package cn.mldn.dbc; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; /* * 本类专门负责数据库的连接与关闭操作,在实例化本类对象时就意味着要进行数据库的开发 * 所以在本类的构造方法里要进行数据库驱动加载与数据库连接取得 * @author mldn * */public class DatabaseConnection {private static final String DBDRIVER = "oracle.jdbc.driver.OracleDriver" ; private static final String DBURL = "jdbc:oracle:thin:@localhost:1521:ORCLBRUS" ; private static final String DBUSER = "c##scott" ; private static final String PASSWORD = "tiger" ; private Connection conn = null; /** 在构造方法里为conn对象进行实例化,可以直接取得数据库的连接对象* 由于所有的操作都是基于数据库完成的,如果数据库取得不到连接,那么也就意味着所有的操作都可以停止了** */public DatabaseConnection(){try {Class.forName(DBDRIVER) ; } catch (ClassNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace(); }try {this.conn = DriverManager.getConnection(DBURL,DBUSER,PASSWORD) ; } catch (SQLException e) {// TODO Auto-generated catch blocke.printStackTrace(); }}/** 取得一个数据库的连接对象* @return Connection实例化对象* */public Connection getConnection(){return this.conn ; }/** 数据库的关闭* */public void close(){if(this.conn != null){try {this.conn.close(); } catch (SQLException e) {// TODO Auto-generated catch blocke.printStackTrace(); }}} }

开发Value Object
现在的程序严格来讲已经给出了四个层次。不同层次之间一定要进行数据的传递,但是既然要操作的是指定的数据表,所以数据的结构必须要与表的结构一一对应,那么自然就可以想到简单Java类(po、to、pojo、vo)。
软件设计-DAO层如何设计(长文-转)
文章图片
image 在实际的工作之中,针对于简单Java类的开发给出如下的要求:
(1) 考虑到日后程序有可能出现的分布式应用问题,所以简单Java类必须要实现java.io.Serializable接口;
(2) 简单java类的名称必须与表名称保存一直;
例如:表名称student_info,类名称为:StudentInfo;
(3) 类中的属性不允许使用基本数据类型,都必须使用基本数据类型的包装类;
因为基本数据类型的数值型默认值是0,而如果是包装类默认值就是null;
(4) 类中可以定义有多个构造方法,但是必须要保留一个无参构造方法;
(5) 类中的属性必须使用private封装,封装后的属性必须提供有setter、getter方法;
将所有的简单Java类保存在vo包中。
范例:定义Emp.java
package cn.mldn.vo; import java.io.Serializable; import java.util.Date; public class Emp implements Serializable {private Integer empno ; private String ename ; private String job ; private Date hiredate ; private Double sal ; private Double comm ; public Integer getEmpno() {return empno; }public void setEmpno(Integer empno) {this.empno = empno; }public String getEname() {return ename; }public void setEname(String ename) {this.ename = ename; }public String getJob() {return job; }public void setJob(String job) {this.job = job; }public Date getHiredate() {return hiredate; }public void setHiredate(Date hiredate) {this.hiredate = hiredate; }public Double getSal() {return sal; }public void setSal(Double sal) {this.sal = sal; }public Double getComm() {return comm; }public void setComm(Double comm) {this.comm = comm; }}

开发数据层
数据层最终是交给业务层进行调用的,所以业务层必须知道数据层的执行标准,
即:业务层需要明确的知道数据层的操作方法,但是不需要知道他的具体实现。每个层之间的相互访问,是需要有标准的存在,标准的存在就是接口的定义。
(JDBC是一个标准)
软件设计-DAO层如何设计(长文-转)
文章图片
image 开发数据层操作标准(也就是接口)
(不同层之间如果要进行访问,那么必须要提供有接口,以定义操作标准,那么对于数据层也是一样的,因为数据层最终是要交给业务层执行,所以需要先定义出数据层接口)
对于数据层的接口给出如下的开发要求:
数据层既然是进行数据操作的,那么就将其保存在dao包下;
既然不同的数据表的操作有可能使用不同的数据层开发,那么就对于数据表进行命名。
对emp表,那么数据层的接口就应该定义为IEmpDAO;
定义IEmpDAO.java:
package cn.mldn.dao; import java.util.*; import cn.mldn.vo.*; /* * 定义emp表的数据层的操作标准 * */public interface IEmpDAO { /** 实现数据的增加操作* @param vo 包含了要增加数据的VO对象* @return 数据保存成功返回true,否则返回false* @throws Exception SQL执行异常* */public boolean doCreate(Emp vo) throws Exception; /** 实现数据的修改操作* @param vo 包含了要修改数据的信息,一定要提供有ID内容* @return 数据保存成功返回true,否则返回false* @throws Exception SQL执行异常* */public boolean doUpdate(Emp vo) throws Exception; /** 执行数据的批量删除操作,所有要删除的数据已Set集合的形式保存* 删除成功返回true(删除的数据个数与要删除的数据个数相同,例如要删除4个,可是要求的是删除5个,则返回false),否则返回false* @param ids 包含了所有要删除的数据ID,不包含有重复内容* @return 删除成功返回true(删除的数据个数与要删除的数据个数相同),否则返回false* @throws Exception SQL执行异常* */public boolean doRemoveBatch(Set ids) throws Exception; /** 根据雇员编号查询雇员信息* @param id 要查询的雇员编号* @return 如果雇员信息存在,则将数据以VO类对象的形式返回,如果雇员数据不存在,则返回null* @throws Exception SQL执行异常* */public Emp findById(Integer id) throws Exception ; /** 查询指定数据表的全部记录,并且以集合的形式返回* @return 如果表中有数据,则所有的数据会封装为VO对象而后利用List集合返回,* 如果没有数据,那么集合的长度为0(size()== 0,不是null)* @throws Exception SQL执行异常* */public List findAll() throws Exception ; /** 分页进行数据的模糊查询,查询结果以集合的形式返回* @param currentPage 当前所在的页* @param lineSize 每页显示的数据行数* @param column 要进行模糊查询的数据列* @param keyWord 模糊查询的关键字* @return 如果表中有数据,则所有的数据会封装为VO对象而后利用List集合返回,* 如果没有数据,那么集合的长度为0(size()== 0,不是null)* @throws Exception SQL执行异常* */public List findAllSplit(Integer currentPage, IntegerlineSize, String column, String keyWord) throws Exception ; /** 进行模糊查询数据量的统计,如果表中没有记录统计的结果就是0* @param column 要进行模糊查询的数据列* @param keyWord 模糊查询的关键字* @return 返回表中的数据量,如果没有数据返回0* @throws Exception* */public Integer getAllCount(String column, String keyWord) throws Exception ; }

对于整个数据层的开发严格来讲就只有两类功能:
-数据更新:建议它的操作方法以doXxx()的形式命名。例如:doCreate()。
-数据查询,对于查询分为两种形式:
(1)查询表中数据:以fingXxx()形式命名。例如:findById()。
(2)统计表中的数据:以getXxx()形式命名。例如:getName()。
数据层实现类
数据层需要被业务层调用,数据层需要进行数据库的执行(PreparedStatement),由于在开发之中一个业务层操作需要执行多个数据层的调用,所以数据库的打开与关闭操作应该由业务层控制会比较合适。
所有的数据层实现类要求保存在dao.impl子包下。
范例:EmpDAOImpl子类:
package cn.mldn.dao.impl; import java.sql.*; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Set; import cn.mldn.dao.IEmpDAO; import cn.mldn.vo.Emp; /* * =======数据层实现类=========== ** */public class EmpDAOImpl implements IEmpDAO {private Connection conn ; //需要利用COnnection对象操作private PreparedStatement pstmt ; /** 如果要想使用数据层进行原子性的功能操作实现,必须要提供有Connection接口对象* 另外,由于开发之中业务层要调用数据层,所以数据库的打开与关闭交由业务层处理* @param conn表示数据库连接对象* */public EmpDAOImpl(Connection conn){this.conn = conn ; }@Overridepublic boolean doCreate(Emp vo) throws Exception {String sql = "INSERT INTO emp(empno,ename,job,hiredate,sal,comm) VALUES (?,?,?,?,?,?)" ; this.pstmt = this.conn.prepareStatement(sql); this.pstmt.setInt(1, vo.getEmpno()); this.pstmt.setString(2, vo.getEname()); this.pstmt.setString(3, vo.getJob()); this.pstmt.setDate(4, new java.sql.Date(vo.getHiredate().getTime())); this.pstmt.setDouble(5, vo.getSal()); this.pstmt.setDouble(6, vo.getComm()); return this.pstmt.executeUpdate() > 0 ; }@Overridepublic boolean doUpdate(Emp vo) throws Exception {String sql = "UPDATE emp SET ename=?,job=?,hiredate=?,sal=?,comm=? WHERE empno=?" ; this.pstmt = this.conn.prepareStatement(sql); this.pstmt.setString(1, vo.getEname()); this.pstmt.setString(2, vo.getJob()); this.pstmt.setDate(3, new java.sql.Date(vo.getHiredate().getTime())); this.pstmt.setDouble(4, vo.getSal()); this.pstmt.setDouble(5, vo.getComm()); this.pstmt.setInt(6, vo.getEmpno()); return this.pstmt.executeUpdate() > 0 ; }/** =====================* 删除是最头疼的,** */@Overridepublic boolean doRemoveBatch(Set ids) throws Exception {if(ids == null || ids.size() == 0){ //没有要删除的数据return false ; }//字符串的拼凑使用StringBufferStringBuffer sql = new StringBuffer() ; sql.append("DELETE FROM emp WHERE empno IN(") ; Iterator iter = ids.iterator() ; while(iter.hasNext()){//删除语句的格式是delete from emp where empno in ("","","",); sql.append(iter.next()).append(",") ; }//删除最后一个逗号,并且添加)sql.delete(sql.length()-1, sql.length()).append(")"); /* conn.prepareStatement()只接受String,所以让* StringBuffer转为String,需要用toString(),* 这是重点强调了的知识点** */this.pstmt = this.conn.prepareStatement(sql.toString()); //pstmt.executeUpdate()返回结果为intreturn this.pstmt.executeUpdate() == ids.size(); }@Overridepublic Emp findById(Integer id) throws Exception {Emp vo = null ; String sql = "SELECT empno,ename,job,hiredate,sal,comm FROM emp WHERE empno=?" ; this.pstmt = this.conn.prepareStatement(sql) ; this.pstmt.setInt(1, id); ResultSet rs = this.pstmt.executeQuery() ; if(rs.next()) {vo = new Emp() ; vo.setEmpno(rs.getInt(1)); vo.setEname(rs.getString(2)); vo.setJob(rs.getString(3)); vo.setHiredate(rs.getDate(4)); vo.setSal(rs.getDouble(5)); vo.setComm(rs.getDouble(6)); }return vo ; }@Overridepublic List findAll() throws Exception {List all = new ArrayList() ; String sql = "SELETE empno,ename,job,hiredate,sal,comm FROM emp" ; this.pstmt = this.conn.prepareStatement(sql) ; ResultSet rs = this.pstmt.executeQuery() ; while(rs.next()) {Emp vo = new Emp() ; vo.setEmpno(rs.getInt(1)); vo.setEname(rs.getString(2)); vo.setJob(rs.getString(3)); vo.setHiredate(rs.getDate(4)); vo.setSal(rs.getDouble(5)); vo.setComm(rs.getDouble(6)); all.add(vo) ; }return all; }/** =======重点难点(需要拼凑sql语句)======* */@Overridepublic List findAllSplit(Integer currentPage, Integer lineSize, String column, String keyWord) throws Exception {List all = new ArrayList() ; String sql = "SELECT * FROM "+ " (SELECT empno,ename,job,hiredate,sal,comm,ROWNUM rn"+ " FROM emp"+ " WHERE " + column + " LIKE ? AND ROWNUM<=?) temp "+ " WHERE temp.rn>? " ; this.pstmt = this.conn.prepareStatement(sql) ; this.pstmt.setString(1, "%" + keyWord + "%"); this.pstmt.setInt(2, currentPage * lineSize); this.pstmt.setInt(3, (currentPage - 1) * lineSize); ResultSet rs = this.pstmt.executeQuery() ; while(rs.next()) {Emp vo = new Emp() ; vo.setEmpno(rs.getInt(1)); vo.setEname(rs.getString(2)); vo.setJob(rs.getString(3)); vo.setHiredate(rs.getDate(4)); vo.setSal(rs.getDouble(5)); vo.setComm(rs.getDouble(6)); all.add(vo) ; }return all; }@Overridepublic Integer getAllCount(String column, String keyWord) throws Exception {String sql = "SELECT COUNT(empno) FROM emp WHERE " + column + " LIKE ?" ; this.pstmt = this.conn.prepareStatement(sql) ; this.pstmt.setString(1, "%" + keyWord + "%"); ResultSet rs = this.pstmt.executeQuery() ; if(rs.next()) {return rs.getInt(1) ; }return null; } }

定义数据层工厂类--DAOFactory
业务层要想进行数据层的调用,那么必须要取得IEmpDAO接口对象,但是不同层之间要想取得接口对象实例,需要使用工厂设计模式,这个工厂类将其保存在factory子包下。
范例:定义工厂类
package cn.mldn.factory; import java.sql.Connection; import cn.mldn.dao.IEmpDAO; import cn.mldn.dao.impl.*; /* * 数据层的实现类--工厂设计类 ** */public class DAOFactory {public static IEmpDAO getIEmpDAOInstance(Connection conn){return new EmpDAOImpl(conn) ; }}

使用工厂的特征就是外层不需要知道具体的子类。
软件设计-DAO层如何设计(长文-转)
文章图片
image 开发业务层
业务层是真正留给外部调用的,可能是控制层,或者是直接调用,既然业务层也是由不同的层进行调用,所以业务层开发的首要任务就是定义业务层的操作标准。
开发业务层标准—IEmpService
业务层也可以称为Service层,既然描述的是emp表的操作,所以名称就定义为IEmpService,并且保存在Service的子包下,但是对于业务层的方法定义并没有明确的要求,只不过个人强烈建议还是写上有意义的统一名称;

范例:定义IEmpService操作标准
package cn.mldn.service; import java.util.List; import java.util.Map; import java.util.Set; import cn.mldn.vo.Emp ; /* * 定义emp表的业务层的执行标准 ** *//** * 定义emp表的业务层的执行标准,此类一定要负责数据库的打开与关闭操作 * 此类可以通过DAOFactory类取得IEmpDAO接口对象 * @author Bruis * */public interface IEmpService {/*** 实现雇员数据的增加操作,本次操作要调用IEmpDAO接口的如下方法* 需要调用IEmpDAO.findById()方法,判断要增加数据的id是否已经存在* 如果现在要增加的数据编号不存在则调用IEmpDAO.doCreate()方法,返回操作的结果* @param vo包含了要增加数据的VO对象* @return 如果增加数据的ID重复或者保存失败,返回false,否则返回true* @throws SQLException*/ public boolean insert(Emp vo) throws Exception ; /*** 实现雇员数据的修改操作,本次要调用IEmpDAO.doUpdate()方法,本次修改属于全部内容的修改* @param vo* @return* @throws SQLException*/ public boolean update(Emp vo) throws Exception ; /*** 执行雇员数据的删除操作,可以删除多个雇员信息,调用IEmpDAO.doRemoveBatch()方法* @param ids包含了全部要删除数据的集合,没有重复数据* @return* @throws SQLException*/ public boolean delete(Set ids) throws Exception ; /*** 根据雇员编号查找雇员的完整信息,调用IEmpDAO.findById()犯法* @param ids* @return 如果找到了则雇员信息已VO对象返回,否则返回null* @throws SQLException*/ public Emp get(Integer ids) throws Exception ; /*** 查询全部雇员信息,调用IEmpDAO.findAll()方法* @return 查询结果以List集合的形式返回,如果没有数据则集合的长度为0* @throws SQLException*/ public List list() throws Exception ; /*** 实现数据的模糊查询与数据统计,要调用IEmpDAO接口的两个方法* 调用IEmpDAO.findAllSplit()方法,查询出所有的表数据,返回的是List* 调用IEmpDAO.getAllCount()方法,查询所有的数据量,返回的Integer* @param currentPage 当前所在页* @param lineSize 每页显示的记录数* @param column 模糊查询的数据列* @param keyWord 关键字** @return 由于需要返回多种数据类型,所以使用Map集合返回,由于类型不统一,所以所有value的类型设置为Object* 如果key = allEmps,value = https://www.it610.com/article/IEmpDAO.findAllSplit()返回结果,List* 如果key = empCount,value = https://www.it610.com/article/IEmpDAO.getAllCount()返回结果,Integer* @throws Exception*/ public Map list(int currentPage,int lineSize,String column,String keyWord) throws Exception ; }

业务层实现类
-业务层实现类的核心功能:
(1)负责控制数据库的打开和关闭,当存在了业务层对象后其目的就是为了操作数据库,即:业务层对象实例化之后就必须准备好数据库连接:
(2)根据DAOFactory调用getIEmpDAOInstance()方法而后取得IEmpDAO接口对象。业务层的实现类编程在dao.impl子包中。
范例:定义EmpServiceImpl子类
package cn.mldn.service.impl; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import cn.mldn.dbc.DatabaseConnection; import cn.mldn.factory.DAOFactory; import cn.mldn.service.IEmpService; import cn.mldn.vo.Emp; /** * 业务层实现子类的编写 * @author Bruis * */public class EmpServiceImpl implements IEmpService { //在这个类的对象内部就提供有一个数据库连接类的实例化对象private DatabaseConnection dbc = new DatabaseConnection() ; @Overridepublic boolean insert(Emp vo) throws Exception {try{//要增加的雇员编号如果不存在,则findById返回的结果就是null,null表示可以进行新雇员的数据增加if(DAOFactory.getIEmpDAOInstance(this.dbc.getConnection()).findById(vo.getEmpno()) == null){return DAOFactory.getIEmpDAOInstance(this.dbc.getConnection()).doCreate(vo) ; }return false ; } catch(Exception e) {throw e ; } finally {this.dbc.close() ; }}@Overridepublic boolean update(Emp vo) throws Exception {try{return DAOFactory.getIEmpDAOInstance(this.dbc.getConnection()).doUpdate(vo) ; } catch(Exception e) {throw e ; } finally {this.dbc.close() ; }}@Overridepublic boolean delete(Set ids) throws Exception {try{return DAOFactory.getIEmpDAOInstance(this.dbc.getConnection()).doRemoveBatch(ids) ; } catch(Exception e) {throw e ; } finally {this.dbc.close() ; }}@Overridepublic Emp get(Integer ids) throws Exception {try{return DAOFactory.getIEmpDAOInstance(this.dbc.getConnection()).findById(ids) ; } catch(Exception e) {throw e ; } finally {this.dbc.close() ; }}@Overridepublic List list() throws Exception {try{return DAOFactory.getIEmpDAOInstance(this.dbc.getConnection()).findAll() ; } catch(Exception e) {throw e ; } finally {this.dbc.close() ; }}@Overridepublic Map list(int currentPage, int lineSize, String column, String keyWord) throws Exception {/** 查看IEmpService的注释* @return 由于需要返回多种数据类型,所以使用Map集合返回,由于类型不统一,所以所有value的类型设置为Object* 如果key = allEmps,value = https://www.it610.com/article/IEmpDAO.findAllSplit()返回结果,List* 如果key = empCount,value = https://www.it610.com/article/IEmpDAO.getAllCount()返回结果,Integer* */try{Map map = new HashMap() ; map.put("allEmps",DAOFactory.getIEmpDAOInstance(this.dbc.getConnection()).findAllSplit(currentPage, lineSize, column, keyWord)) ; map.put("empCount",DAOFactory.getIEmpDAOInstance(this.dbc.getConnection()).getAllCount(column, keyWord) ) ; return map ; } catch(Exception e) {throw e ; } finally {this.dbc.close() ; }} }

不同层之间的访问依靠的就是工厂类和接口进行操作。
软件设计-DAO层如何设计(长文-转)
文章图片
image 定义业务层的工厂类——ServiceFactory
业务层最终依然需要被其他的层所使用,所以需要为其定义工厂类,该类也同
样保存在factory子包下,如果从实际的开发来讲,业务层应该分成两种,
(1) 前台业务逻辑:可以将其保存在service.front包中,工厂类:SercviceFrontFactory包
(2) 后台业务逻辑:可以将其保存在service.back包中,工厂类:ServiceBackFactory
范例:定义ServiceFactory
/** * @param */package cn.mldn.factory; import cn.mldn.service.IEmpService; import cn.mldn.service.impl.EmpServiceImpl; /** * @author Administrator * */public class ServiceFactory {public static IEmpService getIEmpServiceInstance(){return new EmpServiceImpl() ; }}

软件设计-DAO层如何设计(长文-转)
文章图片
image 在实际的编写之中,子类永远都是不可见的,同时在整个操作里面,控制层完全看不到任何的JDBC代码。
然后下面看一下包的结构:(需要导入oracle的jar包-ojdbc6)
软件设计-DAO层如何设计(长文-转)
文章图片
image 下面就来进行测试,测试之前把oracle服务与监听给打开。
测试类在cn.mldn.test包下
package cn.mldn.test; import java.util.Date; import cn.mldn.factory.ServiceFactory; import cn.mldn.vo.Emp; public class TestEmpInsert {public static void main(String[] args) {Emp vo = new Emp() ; vo.setEmpno(8889); vo.setEname("陈冠佑"); vo.setJob("摄影师"); vo.setHiredate(new Date()); vo.setSal(10.0); vo.setComm(0.5); try {System.out.println(ServiceFactory.getIEmpServiceInstance().insert(vo)); } catch (Exception e) {e.printStackTrace(); }} }

运行TestEmpInsert类之前,看一看emp表的数据情况(位于c##scott用户中的emp表):
软件设计-DAO层如何设计(长文-转)
文章图片
image 运行了TestEmpInsert类之后,结果返回true,表面插入数据成功:
软件设计-DAO层如何设计(长文-转)
文章图片
image 此时,再看看emp表的情况:
软件设计-DAO层如何设计(长文-转)
文章图片
image 测试结束,成功插入数据。
下面再来测试一下分页查询:
运行TestEmpSplit.java
package cn.mldn.test; import java.util.Iterator; import java.util.List; import java.util.Map; import cn.mldn.factory.ServiceFactory; import cn.mldn.vo.Emp; public class TestEmpSplit { //这是压制警告@SuppressWarnings("unchecked")public static void main(String[] args) {try {Map map = ServiceFactory.getIEmpServiceInstance().list(2,5,"ename","") ; //取出分页过后的记录条数,需要向下转型为Integerint count = (Integer)map.get("empCount") ; System.out.println("数据库总的记录条数:" + count); List all = (List) map.get("allEmps") ; Iterator iter = all.iterator() ; while(iter.hasNext()) {Emp vo = iter.next() ; System.out.println(vo.getEname() + ", " + vo.getJob() ); }} catch (Exception e) {e.printStackTrace(); }} }

运行结果:
软件设计-DAO层如何设计(长文-转)
文章图片
image 测试成功。
下面来操作Dept表——实现部门操作 现在要求使用部门表(dept)表实现如下的操作功能
(客户所提供的所有的需求都叫做业务)
-【业务层】进行部门数据的添加;
【数据层】判断要增加的部门编号是否存在,如果不存在则可以添加;
【数据层】实现部门数据的保存;
-【业务层】进行部门数据的修改;
【数据层】执行部门数据的修改操作
-【业务层】进行部门数据的删除;
【数据层】执行部门数据删除;
-【业务层】进行部门数据的全部查询;
【数据层】查询全部;
-【业务层】可以根据部门编号查询一个部门完整信息;
【数据层】根据编号查询;
一、定义Dept.java
package cn.mldn.vo; import java.io.Serializable; @SuppressWarnings("serial")public class Dept implements Serializable { private Integer deptno ; private String dname ; private String loc ; public Integer getDeptno() {return deptno; }public void setDeptno(Integer deptno) {this.deptno = deptno; }public String getDname() {return dname; }public void setDname(String dname) {this.dname = dname; }public String getLoc() {return loc; }public void setLoc(String loc) {this.loc = loc; }}

几乎所有的数据表都应该具备有基础CRUD功能(增加、修改全部、删除数据、根据id显示、数据统计),那么这些功能的方法每个接口都要重复定义。
在整个DAO接口定义的过程中,不同的表区别在于:VO类、主键类型。为了避免代码重复,定义一个公共的接口。那么为了解决重复问题,使用泛型实现接口的继承操作。
二、定义接口IDAO
package cn.mldn.dao; import java.util.List; import java.util.Set; import cn.mldn.vo.Emp; /** * 定义公共的DAO操作接口标准,基本的功能包括:增加、修改全部、删除数据、根据编号查询、查询全部、分页显示、数据统计 * @author mldn * * @param 表示要操作的主键类型,由子接口实现 * @param 表示要操作的VO类型,由子接口实现 */@SuppressWarnings("unused")public interface IDAO {/** 实现数据的增加操作* @param vo 包含了要增加数据的VO对象* @return 数据保存成功返回true,否则返回false* @throws Exception SQL执行异常* */public boolean doCreate(V vo) throws Exception; /** 实现数据的修改操作* @param vo 包含了要修改数据的信息,一定要提供有ID内容* @return 数据保存成功返回true,否则返回false* @throws Exception SQL执行异常* */public boolean doUpdate(V vo) throws Exception; /** 执行数据的批量删除操作,所有要删除的数据已Set集合的形式保存* 删除成功返回true(删除的数据个数与要删除的数据个数相同,例如要删除4个,可是要求的是删除5个,则返回false),否则返回false* @param ids 包含了所有要删除的数据ID,不包含有重复内容* @return 删除成功返回true(删除的数据个数与要删除的数据个数相同),否则返回false* @throws Exception SQL执行异常* */public boolean doRemoveBatch(Set ids) throws Exception; /** 根据雇员编号查询雇员信息* @param id 要查询的雇员编号* @return 如果雇员信息存在,则将数据以VO类对象的形式返回,如果雇员数据不存在,则返回null* @throws Exception SQL执行异常* */public V findById(K id) throws Exception ; /** 查询指定数据表的全部记录,并且以集合的形式返回* @return 如果表中有数据,则所有的数据会封装为VO对象而后利用List集合返回,* 如果没有数据,那么集合的长度为0(size()== 0,不是null)* @throws Exception SQL执行异常* */public List findAll() throws Exception ; /** 分页进行数据的模糊查询,查询结果以集合的形式返回* @param currentPage 当前所在的页* @param lineSize 每页显示的数据行数* @param column 要进行模糊查询的数据列* @param keyWord 模糊查询的关键字* @return 如果表中有数据,则所有的数据会封装为VO对象而后利用List集合返回,* 如果没有数据,那么集合的长度为0(size()== 0,不是null)* @throws Exception SQL执行异常* */public List findAllSplit(Integer currentPage, IntegerlineSize, String column, String keyWord) throws Exception ; /** 进行模糊查询数据量的统计,如果表中没有记录统计的结果就是0* @param column 要进行模糊查询的数据列* @param keyWord 模糊查询的关键字* @return 返回表中的数据量,如果没有数据返回0* @throws Exception* */public Integer getAllCount(String column, String keyWord) throws Exception ; }

三、定义IDeptDAO接口
package cn.mldn.dao; import cn.mldn.vo.Dept; public interface IDeptDAO extends IDAO{ }

四、重新定义IEmpDAO接口
package cn.mldn.dao; import java.util.*; import cn.mldn.vo.*; /* * 定义emp表的数据层的操作标准 * */public interface IEmpDAO extends IDAO{}

软件设计-DAO层如何设计(长文-转)
文章图片
image 五、定义DeptDAOImpl子类
package cn.mldn.dao.impl; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Set; import cn.mldn.dao.IDeptDAO; import cn.mldn.vo.Dept; public class DeptDAOImpl implements IDeptDAO {private Connection conn ; private PreparedStatement pstmt ; public DeptDAOImpl(Connection conn){this.conn = conn ; }@Overridepublic boolean doCreate(Dept vo) throws Exception {String sql = "INSERT INTO dept(deptno,dname,loc) VALUES (?,?,?)" ; this.pstmt = this.conn.prepareStatement(sql) ; this.pstmt.setInt(1, vo.getDeptno()); this.pstmt.setString(2, vo.getDname()); this.pstmt.setString(3, vo.getLoc()); return this.pstmt.executeUpdate() > 0; }@Overridepublic boolean doUpdate(Dept vo) throws Exception {String sql = "UPDATE dept SET dname=?,loc=? WHERE deptno=?" ; this.pstmt = this.conn.prepareStatement(sql) ; this.pstmt.setString(1, vo.getDname()); this.pstmt.setString(2, vo.getLoc()); this.pstmt.setInt(3, vo.getDeptno()); return this.pstmt.executeUpdate() > 0; }@Overridepublic boolean doRemoveBatch(Set ids) throws Exception {if(ids == null || ids.size() == 0){ //没有要删除的数据return false ; }//字符串的拼凑使用StringBufferStringBuffer sql = new StringBuffer() ; sql.append("DELETE FROM dept WHERE deptno IN(") ; Iterator iter = ids.iterator() ; while(iter.hasNext()){//删除语句的格式是delete from emp where empno in ("","","",); sql.append(iter.next()).append(",") ; }//删除最后一个逗号,并且添加)sql.delete(sql.length()-1, sql.length()).append(")"); /* conn.prepareStatement()只接受String,所以让* StringBuffer转为String,需要用toString(),* 这是重点强调了的知识点** */this.pstmt = this.conn.prepareStatement(sql.toString()); //pstmt.executeUpdate()返回结果为intreturn this.pstmt.executeUpdate() == ids.size(); }@Overridepublic Dept findById(Integer id) throws Exception {Dept vo = null ; String sql = "SELECT deptno,dname,loc FROM dept WHERE deptno=?" ; this.pstmt = this.conn.prepareStatement(sql) ; this.pstmt.setInt(1, id); ResultSet rs = this.pstmt.executeQuery() ; if(rs.next()){vo = new Dept() ; vo.setDeptno(rs.getInt(1)); vo.setDname(rs.getString(2)); vo.setLoc(rs.getString(3)); }return vo ; }@Overridepublic List findAll() throws Exception {List all = new ArrayList() ; String sql = "SELECT deptno,dname,loc FROM dept" ; this.pstmt = this.conn.prepareStatement(sql) ; ResultSet rs = this.pstmt.executeQuery() ; while(rs.next()){Dept vo = new Dept() ; vo.setDeptno(rs.getInt(1)); vo.setDname(rs.getString(2)); vo.setLoc(rs.getString(3)); all.add(vo); }return all; }@Overridepublic List findAllSplit(Integer currentPage, Integer lineSize, String column, String keyWord)throws Exception {throw new Exception("此方法未使用") ; }@Overridepublic Integer getAllCount(String column, String keyWord) throws Exception {throw new Exception("此方法未使用") ; } }

六、修改DAOFactory类,增加新的接口对象取得方法。
package cn.mldn.factory; import java.sql.Connection; import cn.mldn.dao.IDeptDAO; import cn.mldn.dao.IEmpDAO; import cn.mldn.dao.impl.DeptDAOImpl; import cn.mldn.dao.impl.EmpDAOImpl; /* * 数据层的实现类--工厂设计类 ** */public class DAOFactory {public static IEmpDAO getIEmpDAOInstance(Connection conn){return new EmpDAOImpl(conn) ; }public static IDeptDAO getIDeptDAOInstance(Connection conn){return new DeptDAOImpl(conn) ; }}

七、开发IDeptService接口
package cn.mldn.service; import java.util.List; import java.util.Set; import cn.mldn.vo.Dept; public interface IDeptService {public boolean insert(Dept vo) throws Exception ; public boolean update(Dept vo) throws Exception ; public boolean delete(Set ids) throws Exception ; public List list() throws Exception ; public Dept get(int id) throws Exception ; }

八、实现DeptServiceImpl
package cn.mldn.service.impl; import java.util.List; import java.util.Set; import cn.mldn.dbc.DatabaseConnection; import cn.mldn.factory.DAOFactory; import cn.mldn.service.IDeptService; import cn.mldn.vo.Dept; public class DeptServiceImpl implements IDeptService {private DatabaseConnection dbc = new DatabaseConnection() ; @Overridepublic boolean insert(Dept vo) throws Exception {try{if(DAOFactory.getIDeptDAOInstance(this.dbc.getConnection()).findById(vo.getDeptno()) == null){return DAOFactory.getIDeptDAOInstance(this.dbc.getConnection()).doCreate(vo); }return false; }catch(Exception e){throw e ; }finally{this.dbc.close(); }}@Overridepublic boolean update(Dept vo) throws Exception {try{return DAOFactory.getIDeptDAOInstance(this.dbc.getConnection()).doUpdate(vo); }catch(Exception e){throw e ; }finally{this.dbc.close(); }}@Overridepublic boolean delete(Set ids) throws Exception {try{return DAOFactory.getIDeptDAOInstance(this.dbc.getConnection()).doRemoveBatch(ids); }catch(Exception e){throw e ; }finally{this.dbc.close(); }}@Overridepublic List list() throws Exception {try{return DAOFactory.getIDeptDAOInstance(this.dbc.getConnection()).findAll(); }catch(Exception e){throw e ; }finally{this.dbc.close(); }}@Overridepublic Dept get(int id) throws Exception {try{return DAOFactory.getIDeptDAOInstance(this.dbc.getConnection()).findById(id); }catch(Exception e){throw e ; }finally{this.dbc.close(); }} }

九、修改服务层工厂类
/** * @param */package cn.mldn.factory; import cn.mldn.service.IDeptService; import cn.mldn.service.IEmpService; import cn.mldn.service.impl.DeptServiceImpl; import cn.mldn.service.impl.EmpServiceImpl; /** * @author Administrator * */public class ServiceFactory {public static IEmpService getIEmpServiceInstance(){return new EmpServiceImpl() ; }public static IDeptService getIDeptServiceInstance(){return new DeptServiceImpl(); }}

这里的测试要用到Junit。接下来的文章会介绍到。
【软件设计-DAO层如何设计(长文-转)】原文链接:https://blog.csdn.net/CoderBruis/article/details/72723840

    推荐阅读