DAO(Database Access Object,数据访问对象)的主要功能是数据操作,在程序的标准开发架构中属于数据层的操作。在整个DAO中实际上是以接口为操作标准,即客户端依靠DAO实现的接口进行操作,而服务端要将接口进行具体的实现。DAO由以下几个部分组成:
DatabaseConnection:专门负责数据库的打开与关闭操作的类。
VO:主要由属性、setter、getter方法组成,VO类中的属性与表中的字段相对应,每一个VO类的对象都表示数据库中的一条记录。
DAO:主要定义操作的接口,定义一系列数据库的原子性操作标准,如增加、修改、删除、按ID查询等。
Impl:DAO接口的真实实现类,完成具体的数据库操作,但是不负责数据库的打开和关闭。
Proxy:代理实现类,主要完成数据库的打开和关闭,并且调用真实实现类对象的操作。
Factory:工厂类,通过工厂类取得一个DAO的实例化对象。
下面以一个DAO代码来说明DAO设计模式:
假设数据库中有一个emp表,表中的字段如下:
empno INT(4) PRIMARY KEY
ename VARCHAR2(10)
job VARCHAR2(9)
hiredate DATE
sal FLOAT(7,2)
一.创建VO类——Emp.java:
该类是由属性、setter、getter方法组成,属性与数据库中的字段相对应。
package com.fcy.vo;
import java.util.Date;
public class Emp {
private int empno;
private String ename;
private String job;
private Date hiredate;
private float sal;
public int getEmpno() {
return empno;
}
public void setEmpno(int 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 float getSal() {
return sal;
}
public void setSal(float sal) {
this.sal = sal;
}
}
二.数据库的连接类——DatabaseConnection.java
该类是用来获得数据库的连接和关闭数据库。在任何需要数据库打开或关闭的地方,调用此类即可。
package com.fcy.dbc;
import java.sql.Connection;
import java.sql.DriverManager;
public class DatabaseConnection {
private static final String DBDRIVER="oracle.jdbc.driver.OracleDriver";
private static final String DBURL="jdbc:oracle:thin:@localhost:1521:orcl";
private static final String DBUSER="system";
private static final String DBPASSWORD="admin";
private Connection conn=null;
public DatabaseConnection() throws Exception{
try{
Class.forName(DBDRIVER);
this.conn=DriverManager.getConnection(DBURL,DBUSER,DBPASSWORD);
}catch(Exception e){
throw e;
}
}
public Connection getConnection(){
return this.conn;
}
public void close() throws Exception{
if(this.conn!=null){
try{
this.conn.close();
}catch(Exception e){
throw e;
}
}
}
}
三.定义DAO操作标准_IEmpDAO.java
该类是定义操作的接口。此类中定义了三个操作,当然也可以定义更多的操作。
package com.fcy.dao;
import java.util.List;
import com.fcy.vo.Emp;
public interface IEmpDAO {
/**
* 数据库的增加操作,一般以doXxx的方式命名
* @param emp 要增加的数据对象
* @return 是否增加成功的标记
* @throws Exception 有异常交给被调用处处理
*/
public boolean doCreate(Emp emp)throws Exception;
/**
* 查询全部数据,一般以findXxx的方式命名
* @param keyWord 查询关键字
* @return 返回全部查询结果,每个Emp对象表示表的一条记录
* @throws Exception 有异常交给被调用处处理
*/
public List<Emp> findAll(String keyWord) throws Exception;
/**
* 根据雇员编号查询雇员信息
* @param empno 雇员编号
* @return 雇员的VO对象
* @throws Exception 有异常交给被调用处处理
*/
public Emp findById(int empno) throws Exception;
}
四.真实主题实现类——EmpDAOImpl.java
该类实现了IEmpDAO接口,重写了IEmp DAO接口中的方法。但此类中,并未获取数据库的连接,因此此类中的方法不能用来直接操作数据库。
package com.fcy.dao.impl;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
import com.fcy.dao.IEmpDAO;
import com.fcy.vo.Emp;
public class EmpDAOImpl implements IEmpDAO{
private Connection conn=null; //注意:数据库的连接未获取
private PreparedStatement pstmt=null;
public EmpDAOImpl(Connection conn){
this.conn=conn;
}
@Override
public boolean doCreate(Emp emp) throws Exception {
boolean flag=false;
String sql="insert into emp(empno,ename,job,hiredate,sal)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.setFloat(5, emp.getSal());
if(this.pstmt.executeUpdate()>0){
flag=true;
}
this.pstmt.close();
return flag;
}
@Override
public List<Emp> findAll(String keyWord) throws Exception {
List<Emp> all=new ArrayList<Emp>();
String sql="select empno,ename,job,hiredate,sal from emp where ename like ? or job like ?";
this.pstmt=this.conn.prepareStatement(sql);
this.pstmt.setString(1, "%"+keyWord+"%");
this.pstmt.setString(2, "%"+keyWord+"%");
ResultSet rs=this.pstmt.executeQuery();
Emp emp=null;
while(rs.next()){
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.getFloat(5));
all.add(emp);
}
this.pstmt.close();
return all;
}
@Override
public Emp findById(int empno) throws Exception {
Emp emp=null;
String sql="select empno,ename,job,hiredate,sal from emp where empno=?";
this.pstmt=this.conn.prepareStatement(sql);
this.pstmt.setInt(1, empno);
ResultSet rs=this.pstmt.executeQuery();
if(rs.next()){
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.getFloat(5));
}
this.pstmt.close();
return emp;
}
}
五.代理主题实现类——EmpDAOProxy.java
该类实现了IEmpDAO,是对数据库中数据直接操作类。
该类获取了数据库的连接,同时也负责关闭数据库。并且调用真实实现类(即EmpDAOImpl类)对象的的方法进行数据库操作。
package com.fcy.dao.proxy;
import java.util.List;
import com.fcy.dao.IEmpDAO;
import com.fcy.dao.impl.EmpDAOImpl;
import com.fcy.dbc.DatabaseConnection;
import com.fcy.vo.Emp;
public class EmpDAOProxy implements IEmpDAO{
private DatabaseConnection dbc =null;
private IEmpDAO dao =null;
public EmpDAOProxy() throws Exception{
this.dbc=new DatabaseConnection();
this.dao=new EmpDAOImpl(this.dbc.getConnection());
}
public boolean doCreate(Emp emp) throws Exception{
boolean flag=false;
try{
if(this.dao.findById(emp.getEmpno())==null){
flag=this.dao.doCreate(emp);
}
}catch(Exception e){
throw e;
}finally{
this.dbc.close();
}
return flag;
}
public List<Emp> findAll(String keyWord) throws Exception{
List<Emp> all=null;
try{
all=this.dao.findAll(keyWord);
}catch(Exception e){
throw e;
}finally{
this.dbc.close();
}
return all;
}
public Emp findById(int empno) throws Exception{
Emp emp=null;
try{
emp=this.dao.findById(empno);
}catch(Exception e){
throw e;
}finally{
this.dbc.close();
}
return emp ;
}
}
六.DAO工厂类——DAOFactory.java
该类包含一个静态方法getIEmpDAOInstance(),返回的值是IEmpDAO,用来取得DAO的实例化对象。
package com.fcy.dao.factory;
import com.fcy.dao.IEmpDAO;
import com.fcy.dao.proxy.EmpDAOProxy;
public class DAOFactory {
public static IEmpDAO getIEmpDAOInstance() throws Exception{
return new EmpDAOProxy();
}
}
七.编写测试类:
package com.fcy.dao.test;
import com.fcy.dao.factory.DAOFactory;
import com.fcy.vo.Emp;
public class Test {
public static void main(String args[]) throws Exception{
//按Id查找
DAOFactory.getIEmpDAOInstance().findById(2);
//查找包含关键字的全部记录
DAOFactory.getIEmpDAOInstance().findAll("张三");
//增加记录
Emp emp=null;
for(int x=0;x<5;x++){
emp =new Emp();
emp.setEmpno(100+x);
emp.setEname("张三-"+x);
emp.setJob("老师-"+x);
emp.setHiredate(new java.util.Date());
emp.setSal(500*x);
DAOFactory.getIEmpDAOInstance().doCreate(emp);
}
}
}
在之后的代码编写中,凡是要用到数据库操作的地方,只需要加上
DAOFactory.getIEmpDAOInstance().相应功能的函数名
即可。
八.问题与讨论:
对于有的读者可能纳闷,写了这么一大段代码,与之前把所有的操作写在一个类中,代码多了好多,似乎把代码复杂化了,现对该设计模式进行如下说明:
1.代码中的包:
该设计模式中共用到了6个包:
com.fcy.dbc:该类包用来存放数据库打开与关闭操作的类。实际项目中,可能会有多个不同的数据库,可以为每个数据库写一个类。
com.fcy.vo:用来存放vo类,一张表对应一个vo类。
com.fcy.dao:存放dao操作接口,一张表对应一个操作接口。
com.fcy.dao.impl:存放真实主题实现类类,实现dao操作接口,同样一张表对应一个impl。
com.fcy.dao.proxy:存放代理类,同样实现了dao操作接口,也是一张表对应一个proxy。
com.fcy.dao.factory:存放工厂类,用来生成dao对象。此包中只有一个类DAOFactory,类中的每一个方法都是静态方法,且都返回一个DAO,
2.代理设计的好处:
有人可能会有疑问,impl与proxy明明可以用一个类来完成,为什么要写两个呢?其实,这样做主要是为了降低代码的耦合度,避免代码高度耦合。
关于耦合度说明:在软件设计中,每个模块应尽量保持独立,这样才能使模块复用性达到最大,同时若有一个模块需要改变,其他模块需要做的改变才能最小。这种描述代码模块间独立程度的术语就是耦合度。
3.工厂设计的好处:
工厂类DAOFactory中,函数都用static进行声明,这样做避免了创建对象。在后续的操作中,只需直接调用即可,不需要考虑构造函数的结构。