大师网-带你快速走向大师之路 解决你在学习过程中的疑惑,带你快速进入大师之门。节省时间,提升效率

DAO设计模式,学习笔记

理解分层

前台:显示层和控制层

后台业务层:业务层和数据层,int ,double等业务层用基本类型,数据层用包装类

项目的核心是后台业务层,什么是业务层,什么是数据层?

业务层是整个程序提供的功能,而一个业务层的操作要想完成需要多个数据层的操作,数据层完成的只是一个个原子性的数据库操作,而我们在实际开发中, 每一个操作的业务往往需要牵扯到多个原子性的操作,也就是说所有的原子性的操作业务最终在业务层只能完成,如果业务非常复杂,往往需要一个总业务层,而后牵扯到若干个子业务层,每个子业务层再去操作数据层


by-pxc.png


数据层:又被称为数据访问层 (Data Access Object)DAO,是专门进行数据库的原子性操作,也就是在数据层中最需要控制的PreparedStatement接口的使用。

业务层:又被称为业务中心,业务对象(Business Object,BO),或者叫服务层(Service),核心的操作是调用DAO层的操作以完成整体项目的业务设计

DAO案例

任务:使用emp表(empno,ename,job,hiredate,sal,comm)实现以下操作功能

  • 【业务层】实现雇员的添加,但是要保证被添加的雇员编号不重复;
    |-[数据层]判断要增加的雇员编号是否存在

|-[数据层]如果雇员编号不存在则进行数据的保存操作

  • 【业务层】实现雇员的修改操作
    |-[数据层]执行数据修改操作

  • 【业务层】实现多个雇员的数据删除操作
    |-[数据层]执行多个雇员的数据删除操作

  • 【业务层】可以根据雇员编号找到一个雇员的信息
    |-[数据层]根据雇员编号查询指定的雇员数据

  • 【业务层】查询所有雇员的信息
    |-[数据层]查询全部的雇员数据

  • 【业务层】实现数据的分页显示(模糊查询)同时又可以返回所有的雇员信息
    |-[数据层]雇员数据的分页查询
    |-[数据层]使用COUNT()函数,统计所有雇员的数量

结论:用户所有的需求都是业务层,开发人员必须要根据业务层进行数据层的数据

项目准备

为了方便项目的管理所有的包的父包设置为com.pxc,子包要根据不同模块划分.定义数据库的连接类(DatabaseConnection)专门用于数据可以的连接操作,保存在bdc包中

package com.pxc.dbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
/**\
*用于数据库的连接和关闭操作,在实例化此类的时候就意味着数据库的开发
*所以在此类的构造方法中就要进行数据库驱动加载和数据库的连接取得
*@author PXC
*
*/
public class DatabaseConnection {
private static final String DBDRIVER="com.mysql.jdbc.Driver";
private static final String DBURL="jdbc:mysql://localhost:3306/mybatis";
private static final String USER="root";
private static final String PWD="935377012";
private Connection conn=null;
/**
* 在构造方法里为conn实例化
*/
public DatabaseConnection(){
try {
Class.forName(DBDRIVER);
try {
this.conn=DriverManager.getConnection(DBURL,USER,PWD);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public void close(){
if(this.conn!=null){
try {
this.conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public Connection getConnection(){
return conn;
}
}

开发Value Object

不同层次之间必然要进行数据的传递,但是既然要操作的是指定的数据表,所以数据的结构要与表的结构一一对应,那么自然就想到是简单java类(po,to,pojo,vo)

简单java类有如下的要求

  1. 考虑到以后有可能出现的分布式应用问题,所以简单java类必须要实现java.io.Serializable接口;

  2. 简单java类的名称必须与表名称保持一致。表名称如果是这种stydent_info类名称为StudentInfo

3.类中的字段必须使用private封装,封装好的属性必须提供get和set方法

  1. 类中的属性不允许使用基本数据类型,都必须使用基本数据类型的包装类。原因是基本数据类型的默认值是0,而包装类的默认数据类型是null

  2. 类中必须保留无参构造方法

将所有的简单java类保存在vo包中

开发数据层操作标准

package com.pxc.dao;
import java.security.PublicKey;
import java.util.List;
import java.util.Set;
import com.pxc.vo.Emp;
/ 定义emp表的数据层的操作标准
@author PXC
/
public interface IEmpDAO {
/

实现数据的增加操作 @param emp
@return 数据保存成功返回true @throws Exception sql执行异常
/
public boolean doCreate(Emp emp)throws Exception;//简化开发,严格的话一定要挨个抛
/*
实现数据的修改操作,本次修改是根据id进行全部字段的修改
@param emp 包含了压迫修改的信息,一定要提供id @return 修改成功返回true
@throws Exception sql执行异常 /
public boolean doUpdate(Emp emp)throws Exception;
/
执行数据的批量数据的操作,所有要删除的数据以Set集合形式保存 @param ids 包含了所有要删除的数据ID,不包含重复内容
@return 删除成功返回true(删除的数据个数和要删除的数据个数相同) @throws Exception
/
public boolean doRemoveBatch(Set ids) throws Exception;
/

根据编号查询指定的雇员信息 @param id 要查询的雇员编号
@return 如果雇员信息存在,则将数据以pojo返回,如果不存在则返回null @throws Exception
/
public Emp findById(Integer id)throws Exception;
/*

查询指定表的全部记录,并且以集合的形式返回 @return 如果表中有数据,则所有的数据都会被封装为pojo对象而后利用List集合返回,
如果没有数据则集合的长度为0,不是返回null @throws Exception SQL执行异常
/
public List findAll() throws Exception;
/*
分页进行数据的模糊查询,查询的结果以集合的形式返回
@param currentPage 当前所在的页 @param lineSize 每页显示的行数
@param column 要进行模糊查询的数据列 @param keyWord 模糊查询的关键字
@return 如果表中有数据,则所有的数据都会被封装为pojo对象而后利用List集合返回, 如果没有数据则集合的长度为0,不是返回null
@throws Exception SQL执行异常 /
public List findAllSplit(Integer currentPage,Integer lineSize,String column,String keyWord)throws Exception;
/*
进行模糊查询数据量的统计 @param column 要进行模糊查询的数据列
@param keyWord 模糊查询的关键字 @return 返回表中的数据量,没有数据返回0
@throws Exception SQL执行异常 /
public Integer getAllCount(String column, String keyWord)throws Exception;
}

数据层的实现类

数据层要被业务层调用,数据层要进行数据库的操作,由于在开发中一个业务层操作需要执行多个数据层的操作,所以数据库的打开和关闭操作应该由业务层操作。

所有的数据层数显类要保存在dao.impl子包下

范例:EmpDAOImpl子类

package com.pxc.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 com.pxc.dao.IEmpDAO;
import com.pxc.vo.Emp;
public class EmpDAOImpl implements IEmpDAO {
private Connection conn;//需要利用connection对象
private PreparedStatement pstmt;
/* 如果要使用数据层进行原子性功能的操作实现,必须提供Connection接口对象
另外业务层要调用数据层,所以数据库的打开和关闭交给业务层处理 @param conn 数据库的连接对象
/
public EmpDAOImpl(Connection conn){
this.conn=conn;
}
@Override
public boolean doCreate(Emp emp) throws Exception {
String sql="insert into emp(empno,ename,job,hiredate,sal,comn) values(?,?,?,?,?,?)";
this.pstmt=this.conn.prepareStatement(sql);
this.pstmt.setInt(1, emp.getEmpno());
this.pstmt.setString(2, emp.getEname());
this.pstmt.setString(3, emp.getJob());
this.pstmt.setDate(4, new java.sql.Date(emp.getHiredate().getTime()));
this.pstmt.setDouble(5, emp.getSal());
this.pstmt.setDouble(6, emp.getComm());
return this.pstmt.executeUpdate()>0;
}
@Override
public boolean doUpdate(Emp emp) throws Exception {
String sql="update emp set ename=?,job=?,hiredate=?,sal=?,comn=? where empno=?";
this.pstmt=this.conn.prepareStatement(sql);
this.pstmt.setString(1, emp.getEname());
this.pstmt.setString(2, emp.getJob());
this.pstmt.setDate(3, new java.sql.Date(emp.getHiredate().getTime()));
this.pstmt.setDouble(4, emp.getSal());
this.pstmt.setDouble(5, emp.getComm());
this.pstmt.setInt(6, emp.getEmpno());
return this.pstmt.executeUpdate()>0;
}
@Override
public boolean doRemoveBatch(Set ids) throws Exception {
if(ids==null||ids.size()==0){//没有要删除的数据
return false;
}
StringBuffer sql=new StringBuffer();
sql.append("delete from emp where empno in(");
Iterator iter=ids.iterator();
while(iter.hasNext()){
sql.append(iter.next()+",");
}
sql.delete(sql.length()-1, sql.length()).append(")");
this.pstmt=this.conn.prepareStatement(sql.toString());
return this.pstmt.executeUpdate()==ids.size();
}
@Override
public Emp findById(Integer id) throws Exception {
Emp emp=null;
String sql="select
from emp where empno=?";
this.pstmt=this.conn.prepareStatement(sql);
pstmt.setInt(1,id);
ResultSet rs=pstmt.executeQuery();
if(rs.next()){
emp.setEmpno(rs.getInt(1));
emp.setEname(rs.getString(2));
emp.setJob(rs.getString(3));
emp.setHiredate(rs.getDate(4));
emp.setSal(rs.getDouble(5));
emp.setComm(rs.getDouble(6));
}
return emp;
}
@Override
public List findAll() throws Exception {
List all =new ArrayList();
String sql="select from emp";
this.pstmt=this.conn.prepareStatement(sql);
ResultSet rs=pstmt.executeQuery();
while(rs.next()){
Emp emp=new Emp();
emp.setEmpno(rs.getInt(1));
emp.setEname(rs.getString(2));
emp.setJob(rs.getString(3));
emp.setHiredate(rs.getDate(4));
emp.setSal(rs.getDouble(5));
emp.setComm(rs.getDouble(6));
all.add(emp);
}
return all;
}
@Override
public List findAllSplit(Integer currentPage, Integer lineSize, String column, String keyWord)
throws Exception {
// TODO Auto-generated method stub
return null;
}
@Override
public Integer getAllCount(String column, String keyWord) throws Exception {
String sql="select count(
) from emp where "+column+"like ?";
this.pstmt=this.conn.prepareStatement(sql);
this.pstmt.setString(1,"%"+keyWord+"%");
ResultSet rs=pstmt.executeQuery();
if(rs.next()){
return rs.getInt(1);
}
return 0;
}
}

定义数据层工厂类

package com.pxc.factory;
import java.sql.Connection;
import com.pxc.dao.IDeptDAO;
import com.pxc.dao.IEmpDAO;
import com.pxc.dao.impl.DeptDAOImpl;
import com.pxc.dao.impl.EmpDAOImpl;
public class DAOFactory {
public static IEmpDAO getIEmpDAOInstance(Connection conn){
return new EmpDAOImpl(conn);
}
}

定义业务层执行标准

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

业务层的实现类

业务层实现类的核心功能

  • 负责控制数据库的打开和关闭。业务层对象实例化之后必须准备好数据库的连接
  • 根据DAOFactory调用getIEmpDAO Instance()方法而后取得IEmpDAO接口对象,业务层的实现类保存在dao.impl子包中

    package com.pxc.service.impl;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Set;
    import com.pxc.dbc.DatabaseConnection;
    import com.pxc.factory.DAOFactory;
    import com.pxc.service.IEmpService;
    import com.pxc.vo.Emp;
    public class EmpServiceImpl implements IEmpService {
    // 在这个类的内部就提供一个数据库实例化对象
    private DatabaseConnection dbc = new DatabaseConnection();
    @Override
    public boolean insert(Emp emp) throws Exception {
    // TODO Auto-generated method stub
    try {
    //要增加的雇员编号不存在,findById()返回的结果接受null,null表示可以进行新雇员信息的保存
    if(DAOFactory.getIEmpDAOInstance(this.dbc.getConnection()).findById(emp.getEmpno())==null){
    return DAOFactory.getIEmpDAOInstance(this.dbc.getConnection()).doCreate(emp);
    }
    return false;
    } catch (Exception e) {
    throw e;
    } finally {
    this.dbc.close();
    }

    }
    @Override
    public boolean update(Emp emp) throws Exception {
    // TODO Auto-generated method stub
    try {
    return DAOFactory.getIEmpDAOInstance(this.dbc.getConnection()).doUpdate(emp);
    } catch (Exception e) {
    throw e;
    } finally {
    this.dbc.close();
    }
    }

    @Override
    public boolean delete(Set ids) throws Exception {
    try {
    return DAOFactory.getIEmpDAOInstance(this.dbc.getConnection()).doRemoveBatch(ids);
    } catch (Exception e) {
    throw e;
    } finally {
    this.dbc.close();
    }
    }
    @Override
    public Emp get(Integer id) throws Exception {

    try {
    return DAOFactory.getIEmpDAOInstance(this.dbc.getConnection()).findById(id);
    } catch (Exception e) {
    throw e;
    } finally {
    this.dbc.close();
    }
    }

    @Override
    public java.util.List list() throws Exception {
    try {
    return DAOFactory.getIEmpDAOInstance(this.dbc.getConnection()).findAll();
    } catch (Exception e) {
    throw e;
    } finally {
    this.dbc.close();
    }
    }

    @Override
    public Map<String, Object> List(int currentPage, int lineSize, String column, String keyWord) throws Exception {
    try {
    Map<String, Object> map=new HashMap<String, Object>();
    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();
    }
    }

}

不同层的访问就是依靠的工厂类和接口进行操作,写了接口,取得对象就用工厂类

定义业务层的工厂类

业务层要被控制层调用,所以要为业务层定义工厂类,该类也保存在factory子包下,业务层应该分为两种

  • 前台业务逻辑:可以将其保存在service.front包中,工厂类:serviceFrontFactory
  • 后台业务逻辑:可以将其保存在service.back包中,工厂类:serviceBackFactory
    范例定义serviceFactory(本次不区分前后台)
    package com.pxc.factory;
    import com.pxc.service.IEmpService;
    import com.pxc.service.impl.EmpServiceImpl;
    public class ServiceFactory {
    public static IEmpService getIeEmpServiceInstance(){
    return new EmpServiceImpl();
    }
    }

在实际编写中,子类永远是不可见的,控制层完全看不见数据库的任何操作。

代码测试

最终的业务层是需要用户去调用的,所以测试分为两种。

  • 调用测试
    按照传统方式产生对象,而后调用里面的方式操作,保存在test子包类
    测试案例:

    package com.pxc.test;
    import java.util.Date;
    import com.pxc.factory.ServiceFactory;
    import com.pxc.vo.Emp;
    public class TestEmpInsert {
    public static void main(String[] args) {
    Emp emp=new Emp();
    emp.setEmpno(456);
    emp.setEname("pxc");
    emp.setJob("IT");
    emp.setHiredate(new Date());
    emp.setSal(0.5);
    emp.setComm(10.0);
    try {
    System.out.println(ServiceFactory.getIeEmpServiceInstance().insert(emp));
    } catch (Exception e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    };
    }
    }
  • 利用junit进行测试
    对于这种业务的测试使用junit是最好的选择,首先要选择类或者接口进行测试,本次使用IEmpService

    package com.pxc.test.junit;
    import static org.junit.Assert.*;
    import java.util.Date;
    import java.util.Random;
    import org.junit.Test;
    import com.pxc.factory.ServiceFactory;
    import com.pxc.vo.Emp;
    import junit.framework.TestCase;
    public class IEmpServiceTest {
    private static int empno ;
    static{
    empno=new Random().nextInt(10000);//动态生成empno 的数据
    }
    @Test
    public void testInsert() {
    Emp emp = new Emp();
    emp.setEmpno(empno);
    emp.setEname("pxc2"+empno);
    emp.setJob("IT"+empno);
    emp.setHiredate(new Date());
    emp.setSal(0.5);
    emp.setComm(10.0);
    try {
    TestCase.assertTrue(ServiceFactory.getIeEmpServiceInstance().insert(emp));
    } catch (Exception e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    };
    }
    @Test
    public void testUpdate() {
    Emp emp=new Emp();
    emp.setEmpno(2);
    emp.setEname("pxc5");
    emp.setJob("IT");
    emp.setHiredate(new Date());
    emp.setSal(0.5);
    emp.setComm(10.0);
    try {
    TestCase.assertTrue(ServiceFactory.getIeEmpServiceInstance().update(emp));
    } catch (Exception e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    };
    }
    @Test
    public void testList() {
    try {
    TestCase.assertTrue(ServiceFactory.getIeEmpServiceInstance().list().size()>0);
    } catch (Exception e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
    }
    }

以上就是我在学习DAO时做的笔记,本次只涉及到单表查询,等熟练运用单表查询后,再去考虑多表查询。