crazyox 发表于 2007-9-12 16:13

浅谈DAO模式(一)

最近在整理以前学习JAVA时留下的一些学习笔记,收获波多...现将其中一篇关于DAO模式的学习心得发出来,以供大家分享...


在此先说明一下,在本帖中,我并不会对DAO模式作很详细的讲解(关于DAO模式的更多详细介绍,还请您自己去上网查找一些牛人写的文档 ),如本文标题所示,只是"浅谈"而已.如果你看了本贴之后,能够快速的开发出自己的DAO组件,那就已经是达到了我的目的了...,如果您发现本文有错的地方,请指出来,不胜感激!!!


好,废话少说,让我们进入正题...

DAO简介

在以前常规的应用程序开发当中,如果有涉及到数据持久层的操作,那业务代码将无可避免的嵌入一些与业务无关的语句,如SQL语句,try{..}catch{..}语句...这样,代码将变得很难维护,而且组件复用也无从谈起,违背了OO原则.使用DAO模式能帮我们解决这个问题.DAO(Data Access Objects),也就是数据访问对象的简称.

首先,数据持久层开发人员先把对数据库的操作(也就是一些SQL语句:insert,update...)封装成一个DAO组件,然后发布给业务开发人员使用.则从使用者(业务开发人员)的角度来看,他们对数据的操作,就像是对对象的操作一样,一切都变得简单明了.如,要将一个User对象插入到数据库中,可以这样来做:UserDAO.insert(user);DAO组件会自动帮你把User对象中的属性插入到持久层中.这样一来,业务开发人员再也不用面对那些烦人的SQL,try{..} catch{..}语句了,天大欢喜...


下面以一个实际的例子来说明DAO模式的好处.
假如有一个User类,它有id,name,age等属性,它是一个标准的JAVABEAN.现我们的业务需求是要持久化一个User对象,直接使用JDBC的开发模式,业务开发人员将不得不这样来写:


...
Connection conn = null;
PreparedStatement pre = null;
String sql = "insert into user(id,name,age) values(?,?,?)";
try
{
       Class.forName("yourDBDriver");
       conn = Driver.getConnection();
       pre = conn.prepareStatement(sql);
       pre.setInt(1,user.getId());
       pre.setString(2,user.getName());
       pre.setInt(3,user.getAge());
       pre.executeUpdate();
}
catch(SQLException e)
{
       e.printStackTrace();
}
finally
{
       if(conn != null)
       {
            try
            {
                     conn.close();
            }
            catch(SQLException e)
            {
                     e.printStackTrace();
            }
       }
       if(pre != null)
       {
            try
            {
                     pre.close();
            }
            catch(SQLException e)
            {
                     e.printStackTrace();
            }
       }
}

...

在这些代码中,真正与业务需求(持久化User对象)有关的,才四行代码,其它的都是一些与业务无关的辅助性代码.下面我们使用DAO模式,由于DAO组件已由持久层开发人员开发好了,所以业务开发人员只须以下两行代码就可以持久化一个User对象:
IUserDAO userDAO = (IUserDAO)getUserDAO();//IUserDAO为通用的数据访问接口
userDAO.insert(user);

其中getUserDAO()返回的就是具体的DAO组件.从中我们也可以看出,业务代码只与数据操作接口有关,而与底层实现无关,如果哪天要将应用程序由MySQL系统移植到MSSQL系统,那我们只需修改配置文件中的底层实现配置,而无须修改任何业务逻辑代码!
现在你应该可以稍微领略到DAO模式的威力了吧




看到这里,学过Hibernate的同学可能对我上面所说的不以为然,认为在持久层的操作方面,Hibernate已经帮我们封装得很好了,我们无须再开发自己的DAO组件.没错,Hibernate是一款非常优秀的ORM解决方案,事实上,您可以将其用在您认为可以用的地方.我也极力推荐您这样做.但作为开发人员,特别是JAVA开发者,我们不应极限于某种框架,对一种框架的应用,或者说学习,应是"学习其架构与设计思想"(某位牛人的语录,可惜我一直做不到..:) ,而不是不厌其烦的学习其API怎么用(惭愧,我也是这样...唉.. :).在这里,我之所以要讲解DAO模式,并不是要您在实际开发中使用DAO(您也可以这样做),最主要的目的,是要让您了解什么是设计模式,设计模式怎么来,以及设计模式威力之所在.(当然,还有设计模式所带给开发者的乐趣. )...


又说了一些废话 好,下面让我们来开发一个完整的基于DAO模式的应用程序


Let's go...



crazyox 发表于 2007-9-12 16:14

DAO开发

有一个User类,具有id,name,age,email等属性.在MySQL的dao_db数据库中,有一个与其对应的user表,建表语句如下:


create table user(
id int primary key auto_increment,
name varchar(50) not null,
age int,
email varchar(50));
注意其中的id为自增量.

现在我们要对User对象进行基本的数据操作(insert,update,delete,find).
步骤一:定义User类的DAO接口,并在接口中定义所有数据操作方法.

-----------------IUserDAO.java---------------------
package com.cong.dao;
/**
* User对象在数据库中访问接口
* 定义了对User对象在数据库中的几种基本操作
*
*/
import com.cong.bean.User;
public interface IUserDAO {
       /**
      * 通过ID查找具体的User对象
      * @return User实例 or null
      */
       public User find(int id);
      
       /**
      * 插入User实例到数据表中
      * @return 1 or 0
      */
       public int insert(User user);
      
       /**
      * 更新已有的User数据
      * @return 1 or 0
      */
       public int update(User user);
      
       /**
      * 通过ID删除具体的User对象
      * @return 1 or 0
      */
       public int delete(int id);
      
}

步骤二:编写User类,也就是一个JAVABEAN

-----------------User.java---------------------
package com.cong.bean;
/**
* User类,对应于mysql中的表user;以及mssql中的表user_t.
* 提供三种构造方法.以及各种属性的setXXX和getXXX方法
* 注:id属性为自增量
*/
public class User {
       private int id;
       private String name;
       private int age;
       private String email;
       public User(){}
       public User(String name,int age,String email){
            this.name = name;
            this.age = age;
            this.email = email;
       }
       public User(int id,String name,int age,String email){
            this.id = id;
            this.name = name;
            this.age = age;
            this.email = email;
       }
       public int getAge() {
            return age;
       }
       public void setAge(int age) {
            this.age = age;
       }
       public int getId() {
            return id;
       }
       public void setId(int id) {
            this.id = id;
       }
       public String getEmail() {
            return email;
       }
       public void setEmail(String email) {
            this.email = email;
       }
       public String getName() {
            return name;
       }
       public void setName(String name) {
            this.name = name;
       }
}

[ 本帖最后由 crazyox 于 2007-9-12 16:22 编辑 ]

crazyox 发表于 2007-9-12 16:16

步骤三:编写底层的DAO实现,在这个类中,我将会用到一个能读取系统配置信息的辅助类ConfigReader,其原代码见后面


-----------------UserDAO.java---------------------//mysql数据库
package com.cong.dao.impl.mysql;
/**
* IUserDAO的底层实现类(mysql数据库)
*
*/
import com.cong.bean.User;
import com.cong.dao.IUserDAO;
import com.cong.tool.ConfigReader;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.DriverManager;
import java.sql.ResultSet;
public class UserDAO implements IUserDAO {
       private Connection conn = null;
       private staticString DBDRIVER = ""; //数据库驱动名
       private staticString URL = "";      //数据库URL
       private staticString USER = "";   //用户名
       private staticString PASSWORD = ""; //密码
       public UserDAO(){
            init();
       }
       /**
      * 初始化方法
      * ConfigReader为辅助工具类,通过其静态方法:read(key)可读取系统的配置信息,并返回相应的value
      */
       private void init(){
            try{
                     DBDRIVER = ConfigReader.read("DBDRIVER");
                     URL = ConfigReader.read("URL");
                     USER = ConfigReader.read("USER");
                     PASSWORD = ConfigReader.read("PASSWORD");
            }
            catch(Exception e){
                     e.printStackTrace();
            }
       }
      
       /**
      * 获取数据库的连接
      * @return Connection
      */
       private Connection getConnection(){
            if(conn == null){
                     try{
                            Class.forName(DBDRIVER);                                    //加载数据库驱动程序
                            conn = DriverManager.getConnection(URL,USER,PASSWORD);//获取数据库的连接
                     }
                     catch(Exception e){
                            e.printStackTrace();
                     }
            }
            return conn;
       }
      
       /**
      * 通过ID删除具体的User对象
      * @param id
      * @return 1 or 0
      */
       public int delete(int id) {
            // TODO Auto-generated method stub
            String sql = "delete from user where id='"+id+"'";
            PreparedStatement pre = null;
            try{
                     conn = getConnection();
                     pre = conn.prepareStatement(sql);
                     int status = pre.executeUpdate();
                     return status;
            }
            catch(Exception e){
                     e.printStackTrace();
            }
            finally{
                     if(conn != null){
                            try{
                                 conn.close();
                            }
                            catch(Exception e){
                                 e.printStackTrace();
                            }
                     }
                     if(pre != null){
                            try{
                                 pre.close();
                            }
                            catch(Exception e){
                                 e.printStackTrace();
                            }
                     }
            }
            return 0;
       }
      
       /**
      * 通过ID查找具体的User对象
      * @param id
      * @return User实例 or null
      */
       public User find(int id) {
            // TODO Auto-generated method stub
            
            String sql = "select * from user where id='"+id+"'";
            //User user = null;
            PreparedStatement pre = null;
            ResultSet re = null;
            try{
                     conn = getConnection();
                     pre = conn.prepareStatement(sql);
                     re = pre.executeQuery();
                     User user = new User();
                     while(re.next()){                            //将查询结果封装为User实例
                            user.setId(re.getInt("id"));
                            user.setName(re.getString("name"));
                            user.setAge(re.getInt("age"));
                            user.setEmail(re.getString("email"));
                     }
                     return user;
            }
            catch(Exception e){
                     e.printStackTrace();
            }
            finally{
                     if(conn != null){
                            try{
                                 conn.close();
                            }
                            catch(Exception e){
                                 e.printStackTrace();
                            }
                     }
                     if(pre != null){
                            try{
                                 pre.close();
                            }
                            catch(Exception e){
                                 e.printStackTrace();
                            }
                     }
                     if(re != null){
                            try{
                                 re.close();
                            }
                            catch(Exception e){
                                 e.printStackTrace();
                            }
                     }
            }
            return null;
       }
       /**
      * 插入User实例到数据表中
      * @param user
      * @return 1 or 0
      */
       public int insert(User user) {
            // TODO Auto-generated method stub
            if(user != null){
                     String sql = "insert into user(name,age,email) values(?,?,?) ";
                     PreparedStatement pre = null;
                     try{
                            conn = getConnection();
                            pre = conn.prepareStatement(sql);
                            pre.setString(1,user.getName());
                            pre.setInt(2,user.getAge());
                            pre.setString(3,user.getEmail());
                            int status = pre.executeUpdate();
                            return status;
                     }
                     catch(Exception e){
                            e.printStackTrace();
                     }
                     finally{
                            if(conn != null){
                                 try{
                                          conn.close();
                                 }
                                 catch(Exception e){
                                          e.printStackTrace();
                                 }
                            }
                            if(pre != null){
                                 try{
                                          pre.close();
                                 }
                                 catch(Exception e){
                                          e.printStackTrace();
                                 }
                            }
                     }
            }
            
            return 0;
       }
       /**
      * 更新已有的User数据
      * @param user
      * @return 1 or 0
      */
       public int update(User user) {
            // TODO Auto-generated method stub
            if(user != null){
                     String sql = "update user set name=?,age=?,email=? where id='"+user.getId()+"'";
                     PreparedStatement pre = null;
                     try{
                            conn = getConnection();
                            pre = conn.prepareStatement(sql);
                            pre.setString(1,user.getName());
                            pre.setInt(2,user.getAge());
                            pre.setString(3,user.getEmail());
                            int status = pre.executeUpdate();
                            return status;
                     }
                     catch(Exception e){
                            e.printStackTrace();
                     }
                     finally{
                            if(conn != null){
                                 try{
                                          conn.close();
                                 }
                                 catch(Exception e){
                                          e.printStackTrace();
                                 }
                            }
                            if(pre != null){
                                 try{
                                          pre.close();
                                 }
                                 catch(Exception e){
                                          e.printStackTrace();
                                 }
                            }
                     }
            }
            return 0;
       }
}
-----------------ConfigReader.java---------------------
package com.cong.tool;
/**
* 辅助工具类,可读取系统的配置信息
*/
import java.util.Properties;
import java.io.FileInputStream;
public class ConfigReader {
       //private String value;
       private static Properties p = null;
       static{
            try{
                     p = new Properties();
                     p.load(new FileInputStream("src/config.properties"));//加载配置信息
            }
            catch(Exception e){
                     e.printStackTrace();
            }
       }
      
       public ConfigReader(){}
       /**
      * 静态方法,根据KEY获取对应的VALUE
      * @param key
      * @return String
      * @throws Exception
      */
       public static String read(String key) throws Exception{
            return p.getProperty(key);
       }
}

crazyox 发表于 2007-9-12 16:18

完成了以上三步后,我们就可以在客户端使用以上定义好的DAO类了,下面我们来编写一下测试代码(在这里,我们可以将其称为应用程序,也就是逻辑代码)

系统配置信息


-----------------config.properties---------------------
USERDAO.CLASS.NAME = com.cong.dao.impl.mysql.UserDAO
DBDRIVER = com.mysql.jdbc.Driver
URL = jdbc:mysql://localhost/dao_db
USER = root
PASSWORD = 123
-----------------DaoDemoTest.java---------------------
package com.cong.test;
import com.cong.bean.User;
import com.cong.dao.IUserDAO;
import com.cong.tool.ConfigReader;
public class DaoDemoTest {
       private IUserDAO userDAO;
       public DaoDemoTest(){
            init();
       }
       /**
      * 初始化方法,根据配置信息产生具体的UserDAO实例
      *
      */
       private void init(){
            try{
                     userDAO = (IUserDAO)Class.forName(ConfigReader.read("USERDAO.CLASS.NAME")).newInstance();
            }
            catch(Exception e){
                     e.printStackTrace();
            }
       }
       private IUserDAO getUserDAO(){
            return userDAO;
       }
       public void find(){
            IUserDAO userDAO = getUserDAO();
            User user = userDAO.find(1);
            if(user != null){
                     System.out.println("以下是你要查找的ID为"+user.getId()+"的客户的信息:");
                     System.out.println("ID : "+user.getId());
                     System.out.println("姓名: "+user.getName());
                     System.out.println("年龄: "+user.getAge());
                     System.out.println("邮箱: "+user.getEmail());
            }
            else{
                     System.out.println("没有你要查找的客户信息");
            }
       }
       public void insert(){
            IUserDAO userDAO = getUserDAO();
            User user = new User("cong",20,"cong@163.com");
            int status = userDAO.insert(user);
            if(status > 0){
                     System.out.println("恭喜!注册成功!!");
            }
            else{
                     System.out.println("注册打败!!");
            }
       }
       public void delete(){
            IUserDAO userDAO = getUserDAO();
            int status = userDAO.delete(2);
            if(status > 0){
                     System.out.println("恭喜!!注销成功!!");
            }
            else{
                     System.out.println("注销失败!!");
            }
       }
       public void update(){
            IUserDAO useDAO = getUserDAO();
            User user = new User(1,"kawen",17,"kawen@163.com");
            int status = useDAO.update(user);
            if(status > 0){
                     System.out.println("恭喜!!更新成功!!");
            }
            else{
                     System.out.println("更新失败!!");
            }
       }
      
       public static void main(String[] args){
            DaoDemoTest demo = new DaoDemoTest();
            demo.insert();
            //demo.update();
            //demo.find();
            //demo.delete();
       }
}

系统配置信息运行的时候请别忘了将MySQL的驱动程序加入到您的classpath中...

crazyox 发表于 2007-9-12 16:20

从上面的例子中,我们可以看出,应用程序(DaoDemoTest.java)与DAO组件(UserDAO.java)是完全独立的,事实上,应用程序根本感觉不到DAO组件的存在,它只依赖于通用的DAO接口(IUserDAO.java).如果我们要将应用程序移植到MSSQL数据库中,那我们只须修改配置信息就可以了(假设您已有了MSSQL数据库的DAO实现),而不用修改任何业务代码.
下面我们就来编写MSSQL数据库中的DAO实现.

(因为在MSSQL系统中,user为关键字,所以在这里我们须将表名改为user_t.同里从这点您也可以看出,不管底层数据库怎么变,我们的业务代码都不用做一丝的改变)


-----------------UserDAO.java---------------------//sql2000数据库
package com.cong.dao.impl.mssql;
/**
* IUserDAO的底层实现类(mysql数据库)
*
*/
import com.cong.bean.User;
import com.cong.dao.IUserDAO;
import com.cong.tool.ConfigReader;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.DriverManager;
import java.sql.ResultSet;
public class UserDAO implements IUserDAO {
       private Connection conn = null;
       private staticString DBDRIVER = ""; //数据库驱动名
       private staticString URL = "";      //数据库URL
       private staticString USER = "";   //用户名
       private staticString PASSWORD = ""; //密码
       public UserDAO(){
            init();
       }
       /**
      * 初始化方法
      * ConfigReader为辅助工具类,通过其静态方法:read(key)可读取系统的配置信息,并返回相应的value
      */
       private void init(){
            try{
                     DBDRIVER = ConfigReader.read("DBDRIVER");
                     URL = ConfigReader.read("URL");
                     USER = ConfigReader.read("USER");
                     PASSWORD = ConfigReader.read("PASSWORD");
            }
            catch(Exception e){
                     e.printStackTrace();
            }
       }
      
       /**
      * 获取数据库的连接
      * @return Connection
      */
       private Connection getConnection(){
            if(conn == null){
                     try{
                            Class.forName(DBDRIVER);                                    //加载数据库驱动程序
                            conn = DriverManager.getConnection(URL,USER,PASSWORD);//获取数据库的连接
                     }
                     catch(Exception e){
                            e.printStackTrace();
                     }
            }
            return conn;
       }
      
       /**
      * 通过ID删除具体的User对象
      * @param id
      * @return 1 or 0
      */
       public int delete(int id) {
            // TODO Auto-generated method stub
            String sql = "delete from user_t where id='"+id+"'";
            PreparedStatement pre = null;
            try{
                     conn = getConnection();
                     pre = conn.prepareStatement(sql);
                     int status = pre.executeUpdate();
                     return status;
            }
            catch(Exception e){
                     e.printStackTrace();
            }
            finally{
                     if(conn != null){
                            try{
                                 conn.close();
                            }
                            catch(Exception e){
                                 e.printStackTrace();
                            }
                     }
                     if(pre != null){
                            try{
                                 pre.close();
                            }
                            catch(Exception e){
                                 e.printStackTrace();
                            }
                     }
            }
            return 0;
       }
      
       /**
      * 通过ID查找具体的User对象
      * @param id
      * @return User实例 or null
      */
       public User find(int id) {
            // TODO Auto-generated method stub
            
            String sql = "select * from user_t where id='"+id+"'";
            //User user = null;
            PreparedStatement pre = null;
            ResultSet re = null;
            try{
                     conn = getConnection();
                     pre = conn.prepareStatement(sql);
                     re = pre.executeQuery();
                     User user = new User();
                     while(re.next()){                            //将查询结果封装为User实例
                            user.setId(re.getInt("id"));
                            user.setName(re.getString("name"));
                            user.setAge(re.getInt("age"));
                            user.setEmail(re.getString("email"));
                     }
                     return user;
            }
            catch(Exception e){
                     e.printStackTrace();
            }
            finally{
                     if(conn != null){
                            try{
                                 conn.close();
                            }
                            catch(Exception e){
                                 e.printStackTrace();
                            }
                     }
                     if(pre != null){
                            try{
                                 pre.close();
                            }
                            catch(Exception e){
                                 e.printStackTrace();
                            }
                     }
                     if(re != null){
                            try{
                                 re.close();
                            }
                            catch(Exception e){
                                 e.printStackTrace();
                            }
                     }
            }
            return null;
       }
       /**
      * 插入User实例到数据表中
      * @param user
      * @return 1 or 0
      */
       public int insert(User user) {
            // TODO Auto-generated method stub
            if(user != null){
                     String sql = "insert into user_t(name,age,email) values(?,?,?) ";
                     PreparedStatement pre = null;
                     try{
                            conn = getConnection();
                            pre = conn.prepareStatement(sql);
                            pre.setString(1,user.getName());
                            pre.setInt(2,user.getAge());
                            pre.setString(3,user.getEmail());
                            int status = pre.executeUpdate();
                            return status;
                     }
                     catch(Exception e){
                            e.printStackTrace();
                     }
                     finally{
                            if(conn != null){
                                 try{
                                          conn.close();
                                 }
                                 catch(Exception e){
                                          e.printStackTrace();
                                 }
                            }
                            if(pre != null){
                                 try{
                                          pre.close();
                                 }
                                 catch(Exception e){
                                          e.printStackTrace();
                                 }
                            }
                     }
            }
            
            return 0;
       }
       /**
      * 更新已有的User数据
      * @param user
      * @return 1 or 0
      */
       public int update(User user) {
            // TODO Auto-generated method stub
            if(user != null){
                     String sql = "update user_t set name=?,age=?,email=? where id='"+user.getId()+"'";
                     PreparedStatement pre = null;
                     try{
                            conn = getConnection();
                            pre = conn.prepareStatement(sql);
                            pre.setString(1,user.getName());
                            pre.setInt(2,user.getAge());
                            pre.setString(3,user.getEmail());
                            int status = pre.executeUpdate();
                            return status;
                     }
                     catch(Exception e){
                            e.printStackTrace();
                     }
                     finally{
                            if(conn != null){
                                 try{
                                          conn.close();
                                 }
                                 catch(Exception e){
                                          e.printStackTrace();
                                 }
                            }
                            if(pre != null){
                                 try{
                                          pre.close();
                                 }
                                 catch(Exception e){
                                          e.printStackTrace();
                                 }
                            }
                     }
            }
            return 0;
       }
}

crazyox 发表于 2007-9-12 16:21

然后修改配置信息


-----------------config.properties---------------------
USERDAO.CLASS.NAME = com.cong.dao.impl.mssql.UserDAO
DBDRIVER = com.microsoft.jdbc.sqlserver.SQLServerDriver
URL = jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=dao_db
USER = sa
PASSWORD = 123

(注意运行时请将相关的驱动程序加入到您的CLASSPATH中)

关于基本的DAO模式就先讲到这里吧,下次我还会说说高级一点的DAO模式(工厂模式),以及JDBC模板...

如果上面有错的地方,请给我留言好吗?谢谢啦...

一口气写了这么多,累...呵呵

powerwind 发表于 2007-9-12 19:13

谢谢分享,very good!

如果把DAO类执行SQL语句的地方进行封装(包装)会更好

crazyox 发表于 2007-9-12 22:43

呵呵...
回LS,在下一主题中,我会对其进行封装的,也就是JDBC模板以及工厂模式啦...
在这里只是想尽可能简单的说明DAO模式,所以就没有封装咯...

powerwind 发表于 2007-9-13 00:14

原帖由 crazyox 于 2007-9-12 22:43 发表 http://www.gdutbbs.com:8080/images/common/back.gif
呵呵...
回LS,在下一主题中,我会对其进行封装的,也就是JDBC模板以及工厂模式啦...
在这里只是想尽可能简单的说明DAO模式,所以就没有封装咯...

期待。。。

hjack 发表于 2007-9-14 02:12

不错。支持一下。

期待更多分享。。。

活在阳光下 发表于 2007-9-28 13:44

我也来支持一下!

wool王 发表于 2007-10-16 00:13

看了LZ的帖子~我好像又年轻了几岁~~

好怀念大学的学习生活~

期待LZ投入更大的热情分享自己的学习和实践经验~
页: [1]
查看完整版本: 浅谈DAO模式(一)